DRT modified file to work on any computer
2022-01-17 Some files appeared to be missing to run this notebook.
Trying to get all to run
Overview
This is a pipeline for differential analysis of RNASeq data from
SKMEL5 sublines using DESeq2 statistical package. Three sublines: SC01
(regressing), SC07 (stationary) and SC10
(expanding) were analyzed for gene expression differences. In
addition, time course changes in 8uM PLX4720 were also performed for
each subline. Time points are: 0, 3d, 8d. The differential analysis will
be performed based on the contrasts defined below. General steps for the
analysis are:
###1. Read counts table: + Could be read directly as a csv/txt file.
+ Alignment and read counts could be done within R environment to create
read counts table. 1. Define working directory, load the required
libraries. 2. Get read counts table. Read the raw counts file processed
by featureCounts. The fastq files were aligned with HiSat2, and the read
counts were obtained using featureCounts of Rsubread packages.
pkgs <- c("BiocManager","DESeq2","org.Hs.eg.db","clusterProfiler","HDO.db",
"pheatmap","ggnewscale","PoiClaClu","enrichR","gtable","Rmisc")
source("getReqdPkgs.r")
getReqdPkgs(pkgs)
suppressPackageStartupMessages(expr={
library(plyr)
library(dplyr)
library(ggplot2)
library(ggnewscale)
library(reshape2)
library(DESeq2)
library(ggrepel)
library(pheatmap)
library(org.Hs.eg.db)
library(clusterProfiler)
library("RColorBrewer")
library(enrichR)
library(biomaRt)
library(Rmisc)
})
SAVEFILES <- FALSE
d <- read.csv("../data/featureCounts_matrix_all.csv", header=T, sep=",")
#Rename columns
cols <- c("ensembl_gene_id", "SC01_day0_rep1", "SC01_day0_rep2", "SC01_day0_rep3",
"SC01_day3_rep1", "SC01_day3_rep2", "SC01_day3_rep3",
"SC01_day8_rep1", "SC01_day8_rep2", "SC01_day8_rep3",
"SC07_day0_rep1", "SC07_day0_rep2", "SC07_day0_rep3",
"SC07_day3_rep1", "SC07_day3_rep2", "SC07_day3_rep3",
"SC07_day8_rep1", "SC07_day8_rep2", "SC07_day8_rep3",
"SC10_day0_rep1", "SC10_day0_rep2", "SC10_day0_rep3",
"SC10_day3_rep1", "SC10_day3_rep2", "SC10_day3_rep3",
"SC10_day8_rep1", "SC10_day8_rep2", "SC10_day8_rep3")
names(d) <- cols
ensembl <- useEnsembl("ensembl", mirror="useast")
mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
genes <- d$ensembl_gene_id
G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
filters= "ensembl_gene_id",
values=genes,
mart=mart)
GE_data <- merge(d, G_list, by = "ensembl_gene_id")
d <- GE_data[, -1]
d <- d[c(28, seq(1:27))]
rownames(d) <- make.names(d$hgnc_symbol, unique = T)
d <- d[, 2:28]
# remove genes with <5 counts in all samples
d <- d[apply(d, 1, function(x) all(x > 5)),]
countdata <- d
# baseline <- c(1,2,3,10,11,12,19,20,21)
# treat3d <- c(4,5,6,13,14,15,22,23,24)
# treat8d <- c(7,8,9,16,17,18,25,26,27)
# # define the groups by subclones
# sc01 <- c(baseline[1:3], treat3d[1:3], treat8d[1:3])
# sc07 <- c(baseline[4:6], treat3d[4:6], treat8d[4:6])
# sc10 <- c(baseline[7:9], treat3d[7:9], treat8d[7:9])
# # Get the countdata specific to conditions:
# # countdata <- countdata[,c(baseline)]
# rownames(countdata) <- d[,"ensembl_gene_id"]
head(countdata)
Identifying different ion channel gene lists
###2. Convert counts table to DESeq2 object. Convert counts table to
object for DESeq2 or any other analysis pipeline. This step will require
to prepare data object in a form that is suitable for analysis in DESeq2
pipeline: we will need the following to proceed:
- countdata: a table with the read/fragment counts.
- coldata: a table with information about the samples.
Using the matrix of counts and the sample information table, we need
to construct the DESeqDataSet object, for which we will use
DESeqDataSetFromMatrix…..
1. Define the samples and treatment conditions.
condition <- c("0", "3", "8")
treatment <- rep(condition, each=3) # Three biological replicates
unique(treatment)
[1] "0" "3" "8"
cell <- c("SC01", "SC07","SC10") #sublines used for the analysis
cellName <- rep(cell, each=3)
coldata <- data.frame(cell=rep(cellName), treatment=rep(treatment, each=3))
group = factor(paste(coldata$cell, coldata$treatment, sep="."))
coldata$group = group
1. Pre-filtering and normalization.
Pre-filtering and normalization is required to remove lowly expressed
genes.
dds2 <- dds[rowSums(counts(dds)) > 18, ] # remove rows with minimum of 2 read per condition
nrow(dds2)
[1] 14944
# save(dds2, file = "DDS_SC-1,7,10_cell-treat-int.RData")
# load("DDS_SC-1,7,10_cell-treat-int.RData")
2. Visualize sample-to-sample distances.
We could use Principal Component Analysis (PCA) to visualize
relationships between samples.
rld <- rlog(dds2, blind = FALSE)
# save(rld, file = "RLD_SC-1,7,10_0,3,8d_20180701.RData")
# load("RLD_SC-1,7,10_0,3,8d_20180701.RData")
plotPCA(rld, intgroup = c("cell", "treatment"), ntop=5000)
using ntop=5000 top features by variance

## Use prcomp function
# Colored by cell line, shape by time point, lines connecting time
pca_DEseq <- prcomp(t(assay(rld)))
pca_DEseq_perc <- round(100*pca_DEseq$sdev^2/sum(pca_DEseq$sdev^2),1)
pca_DEseq_df <- data.frame(PC1 = pca_DEseq$x[,1],
PC2 = pca_DEseq$x[,2],
sample = colnames(assay(rld)),
cell.line = rep(c("SC01", "SC07", "SC10"), each = 9),
day = rep(c("Day0", "Day3", "Day8"), each = 3),
replicate = rep(c("Rep1", "Rep2", "Rep3"), times=9))
pca_DEseq_means <- ddply(pca_DEseq_df, .(cell.line, day), summarise, meanPC1 = mean(PC1), meanPC2 = mean(PC2))
ggplot(pca_DEseq_df, aes(PC1,PC2, color = cell.line))+
geom_point(aes(shape = day), size=5) +
geom_path(data = pca_DEseq_means,
aes(x=meanPC1, y=meanPC2,
color=cell.line), arrow = arrow(),
size = 2) +
labs(x=paste0("PC1 (",pca_DEseq_perc[1],"% variance)"), y=paste0("PC2 (",pca_DEseq_perc[2],"% variance)")) +
theme_bw() + ggtitle("PCA - Subclones in Time") +
theme(legend.text = element_text(size = 12),
plot.title = element_text(size = 14,
hjust = 0.5,
face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
legend.position = "bottom",
axis.title=element_text(size=12, face="bold"))
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

NA
NA
NA
4. Differential Expression Analysis.
Always make sure to use the unnormalized raw counts for this. We will
use DESeq function to perform differential analysis between samples;
Unless specified, the analysis is between the last group and the first
group. Different comparison can be done using ‘contrast’ argument. Steps
involved underneath:
- estimation of size factors (controls for differences in sequencing
depth of the samples)
- estimation of dispersion values for each gene,
- fitting a generalized linear model
1. Running the differential expression pipeline.
design(dds2) = ~ cell + treatment + cell:treatment
dds <- DESeq(dds2, test = "LRT", reduced = ~ cell + treatment)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
# save(dds, file = "DESeq_SC1,7,10_Timecourse_LRT.RData")
# load("DESeq_SC1,7,10_Timecourse_LRT.RData")
# dds
2. Building the results table.
By default, results will extract the estimated log2 fold changes and
p values for the last variable in the design formula. If there are more
than 2 levels for this variable, results will extract the results table
for a comparison of the last level over the first level.
# Esimate the differences between groups by: # a) Lowering the FDR (padj) or (b) raise the log2 fold change.
resultsNames(dds)
[1] "Intercept" "cell_SC07_vs_SC01" "cell_SC10_vs_SC01" "treatment_3_vs_0" "treatment_8_vs_0"
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
# alpha = FDR adjusted p value cutoff
res <- results(dds, alpha = 0.001)
summary(res)
out of 14944 with nonzero total read count
adjusted p-value < 0.001
LFC > 0 (up) : 3918, 26%
LFC < 0 (down) : 4377, 29%
outliers [1] : 2, 0.013%
low counts [2] : 290, 1.9%
(mean count < 19)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
resOrdered <- res[order(res$pvalue),]
rdata = as.data.frame(res)
Differential expression: days 0 to 8
Change significant log2 fold change to 1.585 (== 3-fold change in
log2 space).
res_0to8d <- results(dds, name="treatment_8_vs_0", cooksCutoff = 0.99,
independentFiltering = TRUE, alpha = 0.05, pAdjustMethod = "BH")
summary(res_0to8d)
out of 14944 with nonzero total read count
adjusted p-value < 0.05
LFC > 0 (up) : 5580, 37%
LFC < 0 (down) : 5275, 35%
outliers [1] : 23, 0.15%
low counts [2] : 0, 0%
(mean count < 10)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# order results table by the smallest adjusted p value:
res_0to8d <- res_0to8d[order(res_0to8d$padj),]
results_0to8d <- as.data.frame(res_0to8d)
results_0to8d <- mutate(results_0to8d, sig=ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange > 1.585, "Upregulated", ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange < -1.585, "Downregulated", "Not Significant")))
row.names(results_0to8d) <- row.names(res_0to8d)
head(results_0to8d)
DEgenes_0to8d <- results_0to8d[which(abs(results_0to8d$log2FoldChange) > log2(1.5) & results_0to8d$padj < 0.05),]
if(SAVEFILES) write.csv(DEgenes_0to8d, file="~/Desktop/DEgenes_0to8d.csv")
Volcano plot
volcano <- ggplot(results_0to8d, aes(log2FoldChange, -log10(pvalue))) +
geom_point(aes(col = sig)) + theme_bw() +
scale_color_manual(values = c("red", "grey", "green3")) +
# ggtitle("Volcano Plot of Untreated vs Idling") +
labs(x="log2(Fold Change)", y="Log(Odds Ratio)") +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12),
axis.title=element_text(size=12),
legend.position = "none")
volcano

# volcano + ggrepel::geom_text_repel(data=results_0to8d[1:10, ],
# ggplot2::aes(label=rownames(results_0to8d[1:10, ])))
# save(results_0to8d, file="untreatedIdling_DEA.RData")
DEgenes_0to8d <- DEgenes_0to8d[order(abs(DEgenes_0to8d$log2FoldChange),DEgenes_0to8d$sig, decreasing = TRUE),]
temp <- DEgenes_0to8d[DEgenes_0to8d$baseMean > 300,]
temp <- temp[abs(temp$log2FoldChange)>2,]
if(SAVEFILES) write.csv(DEgenes_0to8d, file = "DEgenes_0to8d.csv")
Generating Ion Channel Specific Gene Dataframes
test <- assay(dds)
types <- c("ATP", "TRP", "GABR", "CRACR", "SLC", "KCN", "CACN", "GRI", "ABC", "SCN", "TRP", "RIC3", "CHRND", "RYR")
samples <- c("SC01_day0", "SC01_day3", "SC01_day8", "SC07_day0", "SC07_day3", "SC07_day8", "SC10_day0", "SC10_day3", "SC10_day8")
test <- test[grep(paste(types, collapse="|"), rownames(test)),]
test1 <- sapply(samples, function(x) rowMeans(test[, grep(x, colnames(test))]))
rownames(test1) <- rownames(test)
test1 <- test1[order(rownames(test1)),]
test2 <- as.data.frame(test1)
test2["l2FC_SC01_0to8"] <- log2(test2["SC01_day8"]/test2["SC01_day0"])
test2["l2FC_SC07_0to8"] <- log2(test2["SC07_day8"]/test2["SC07_day0"])
test2["l2FC_SC10_0to8"] <- log2(test2["SC10_day8"]/test2["SC10_day0"])
test3 <- subset(test2, l2FC_SC01_0to8 > 1 & l2FC_SC07_0to8 > 1 & l2FC_SC10_0to8 > 1)
test4 <- subset(test2, l2FC_SC01_0to8 > 1 | l2FC_SC07_0to8 > 1 | l2FC_SC10_0to8 > 1)
# write.csv(x = test2, file = "all_ionChannel_Expression.csv")
# write.csv(x = test3, file = "allUpreg_ionChannel_Expression.csv")
# write.csv(x = test4, file = "atLeastOneUpreg_ionChannel_Expression.csv")
test5 <- log2(test4[, 1:9]+1)
pheatmap(test5, cluster_cols = F, cluster_rows = F)

test6 <- log2((test3[,1:9])+1)
pheatmap(test6, cluster_rows = F, cluster_cols = F)

test7 <- test5[rowSums(test5)>30,]
pheatmap(test7, cluster_rows = F, cluster_cols = F)

# load(file="untreatedIdling_DEA.RData")
OrgDB <- org.Hs.eg.db
upreg_genes <- subset(results_0to8d, padj<0.05 & log2FoldChange>2)
downreg_genes <-subset(results_0to8d, padj<0.05 & log2FoldChange<(-2))
geneList_up <- as.vector(upreg_genes$log2FoldChange)
names(geneList_up) <- rownames(upreg_genes)
geneList_down <- as.vector(downreg_genes$log2FoldChange)
names(geneList_down) <- rownames(downreg_genes)
genes_up <- as.vector(rownames(upreg_genes))
genes_down <- as.vector(rownames(downreg_genes))
# names(geneList) <- rownames(results_0to8d)
genes_up_ENTREZID <- bitr(genes_up, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
genes_down_ENTREZID <- bitr(genes_down, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
# Group GO
ggo_up <- clusterProfiler::groupGO(gene = genes_up_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
level = 3,
readable = TRUE)
ggo_up_df <- as.data.frame(ggo_up)
ggo_up_df <- ggo_up_df[order(-ggo_up_df$Count),]
ggo_down <- clusterProfiler::groupGO(gene = genes_down_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
level = 3,
readable = TRUE)
# View(as.data.frame(ggo_down))
# GO over-representation test
ego_genesUp <- clusterProfiler::enrichGO(gene = genes_up_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
qvalueCutoff = 0.05,
readable = TRUE)
# View(as.data.frame(ego_genesUp))
ego_genesDown <- clusterProfiler::enrichGO(gene = genes_down_ENTREZID,
OrgDb = OrgDB,
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
qvalueCutoff = 0.05,
readable = TRUE)
# View(as.data.frame(ego_genesDown))
# kk_genesUp <- enrichKEGG(gene = genes_up_ENTREZID,
# organism = 'hsa',
# pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesUp))
#
# kk_genesDown <- enrichKEGG(gene = genes_down_ENTREZID,
# organism = 'hsa',
# pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesDown))
# ego_GSEA_up <- gseGO(geneList = geneList_up,
# OrgDb = OrgDB,
# ont = "BP",
# nPerm = 1000,
# minGSSize = 100,
# maxGSSize = 500,
# pvalueCutoff = 0.05,
# verbose = FALSE)
# barplot(ggo_up, order=T)
# barplot(ggo_down)
enrichplot::dotplot(ego_genesUp) + ggtitle("GO Over-representation Upregulated Genes") +
labs(x="Gene Ratio", y="GO Terms") +
theme(legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12, face="bold"))

enrichplot::dotplot(ego_genesDown) + ggtitle("GO Over-representation Downregulated Genes") +
labs(x="Gene Ratio", y="GO Terms") +
theme(legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12, face="bold"))

# emapplot(ego_genesUp)
# emapplot(ego_genesDown)
enrichplot::cnetplot(ego_genesUp, categorySize="pvalue", color.params = list(foldChange = geneList_up))
Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

enrichplot::cnetplot(ego_genesDown, categorySize="pvalue", color.params = list(foldChange = geneList_down))
Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

ego_genesUp_df <- as.data.frame(ego_genesUp)
egoUp <- ego_genesUp_df[order(-ego_genesUp_df$Count),]
# sorted_egoUp_top10 <- head(egoUp, 10)
egoUp_genes <- strsplit(egoUp$geneID, "/", fixed=TRUE)
# egoUp_top10_genes_all <- unlist(strsplit(head(egoUp, 10)$geneID, "[/]"))
# egoUp_top10_genes_group <- strsplit(sorted_egoUp_top10$geneID, "[/]")
# egoUp_top10_genes_unique <- unique(egoUp_top10_genes)
# table(egoUp_top10_genes)
# egoUp_genesByGroup <- as.data.frame(t(plyr::ldply(egoUp_top10_genes_group, rbind)))
# colnames(egoUp_genesByGroup) <- sorted_egoUp_top10$Description
# egoUp_genesByGroup_ionOnly <- egoUp_genesByGroup[,c(1:6,8:10)]
# write.csv(egoUp_genesByGroup, file="top10GOtermsUpregulated_geneMembership.csv")
# ionGenes <- unique(unlist(egoUp_genesByGroup_ionOnly))
#
# ensembl = useEnsembl(biomart="ensembl", dataset="hsapiens_gene_ensembl")
# IDs <- as.character(ionGenes)
# geneUpID <- names(geneList_up)
# geneDownID <- names(geneList_down)
# genedesc_ion <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = IDs, mart =ensembl)
# write.csv(genedesc_ion, file = "ionChannelGenes_description.csv")
# genedesc_Up <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneUpID, mart =ensembl)
# write.csv(genedesc_Up, file = "upregulatedGenes_description.csv")
# genedesc_Down <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneDownID, mart =ensembl)
# write.csv(genedesc_Down, file = "downrgulatedGenes_description.csv")
geneList_all <- as.vector(results_0to8d$log2FoldChange)
names(geneList_all) <- rownames(results_0to8d)
a <- names(geneList_all)
genes_ENTREZID <- bitr(a, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:many mapping between keys and columns
Warning: 10.54% of input gene IDs are fail to map...
names(geneList_all) <- genes_ENTREZID
gene_df <- data.frame(Entrez=names(geneList_all), HGNC=a, FC=geneList_all)
gene_df <- gene_df[abs(gene_df$FC) > 1,]
gene_df$group <- "upregulated"
gene_df$group[gene_df$FC < 0] <- "downregulated"
gene_df$othergroup <- "A"
gene_df$othergroup[abs(gene_df$FC) > 2] <- "B"
formula_res <- compareCluster(Entrez~group+othergroup, data=gene_df, fun="enrichKEGG")
head(as.data.frame(formula_res))
NA
3. Exploring Results
plotMA(res, ylim=c(-2,2))

plotCounts(dds, gene=which.min(res$padj), intgroup="treatment")

Log normalize results
# normalizedCounts <- t( t(counts(dds)) / sizeFactors(dds) )
#log2 normalized counts
rld2 <- rlog(dds, blind = FALSE)
# save(rld2, file = "RLD2_SC1,7,10_Timecourse_hmap.RData")
# load("RLD2_SC1,7,10_Timecourse_hmap.RData")
Clustering
sampleDists <- dist(t(assay(rld2)))
sampleDists
SC01_day0_rep1 SC01_day0_rep2 SC01_day0_rep3 SC01_day3_rep1 SC01_day3_rep2 SC01_day3_rep3 SC01_day8_rep1
SC01_day0_rep2 20.64176
SC01_day0_rep3 18.19293 20.22538
SC01_day3_rep1 49.40193 50.31301 48.91695
SC01_day3_rep2 49.71034 50.26004 49.81799 18.62486
SC01_day3_rep3 49.76155 51.27548 49.96769 18.78657 18.38885
SC01_day8_rep1 62.59359 63.01410 62.85700 32.82065 30.94772 31.44917
SC01_day8_rep2 61.71897 62.55664 61.74415 31.23053 30.59913 30.62985 18.73472
SC01_day8_rep3 61.80263 62.20672 61.81830 31.97443 30.71802 31.15294 18.44927
SC07_day0_rep1 81.89021 82.20006 81.04377 88.57987 89.20801 89.30811 92.42588
SC07_day0_rep2 81.80699 82.20748 81.28570 88.75296 89.12160 89.21459 92.00426
SC07_day0_rep3 81.87195 81.87247 80.68421 88.82137 89.71360 89.74738 93.26375
SC07_day3_rep1 85.61074 85.98940 84.75355 72.66767 73.41897 73.65191 73.24393
SC07_day3_rep2 86.14496 85.54535 85.04926 73.05334 73.23090 73.98050 73.11989
SC07_day3_rep3 85.64684 86.07900 85.21303 72.75165 72.77774 73.15245 71.75449
SC07_day8_rep1 80.85517 80.83147 79.81064 68.49057 69.16763 69.63491 69.64733
SC07_day8_rep2 81.78387 81.86172 81.53618 68.99246 68.65818 69.49494 67.67811
SC07_day8_rep3 81.96127 82.96786 82.07631 69.32061 69.04146 69.60331 67.94028
SC10_day0_rep1 83.07778 82.99836 82.08754 91.37875 92.05715 92.14807 95.58351
SC10_day0_rep2 82.30597 82.41462 81.52323 90.67633 91.20000 91.27253 94.48343
SC10_day0_rep3 83.38418 83.05678 81.96660 90.82899 91.31496 91.65195 94.96023
SC10_day3_rep1 94.56260 95.04098 94.07558 78.96119 79.09299 79.16448 77.51958
SC10_day3_rep2 94.54421 94.56941 93.82024 78.90942 79.24978 79.39973 77.95598
SC10_day3_rep3 93.64285 93.44536 92.79105 78.81335 79.42332 79.33632 78.07884
SC10_day8_rep1 87.27738 86.98355 86.05409 69.06215 69.88851 69.79785 65.85610
SC10_day8_rep2 87.47739 86.47776 86.25693 69.45772 69.72791 70.04895 65.80600
SC10_day8_rep3 86.88310 86.54205 85.74647 68.89336 69.55039 69.66580 65.73789
SC01_day8_rep2 SC01_day8_rep3 SC07_day0_rep1 SC07_day0_rep2 SC07_day0_rep3 SC07_day3_rep1 SC07_day3_rep2
SC01_day0_rep2
SC01_day0_rep3
SC01_day3_rep1
SC01_day3_rep2
SC01_day3_rep3
SC01_day8_rep1
SC01_day8_rep2
SC01_day8_rep3 18.69642
SC07_day0_rep1 91.45517 91.76196
SC07_day0_rep2 91.40235 91.45590 18.52372
SC07_day0_rep3 92.10820 92.37462 19.00827 20.11731
SC07_day3_rep1 72.19122 72.59415 54.91487 55.09966 54.93530
SC07_day3_rep2 72.53745 72.64698 55.77395 55.87724 56.04278 20.64662
SC07_day3_rep3 71.51043 71.58790 55.65100 55.01141 56.47374 20.64008 21.57694
SC07_day8_rep1 68.65155 68.66079 66.69991 67.10675 66.19018 37.27740 37.68364
SC07_day8_rep2 67.75896 67.38623 68.72277 68.18289 69.22524 39.25933 38.47343
SC07_day8_rep3 67.80367 67.67581 68.79488 68.20172 69.41487 38.92660 39.81639
SC10_day0_rep1 94.46929 94.69079 52.01423 52.35396 51.88605 74.67691 75.29208
SC10_day0_rep2 93.50482 93.70891 51.60927 51.84052 51.63913 74.13980 74.80425
SC10_day0_rep3 93.97482 94.02969 53.24466 53.87363 53.18749 74.38570 74.37680
SC10_day3_rep1 76.82909 77.16050 70.15360 69.88040 70.84455 48.48969 48.87908
SC10_day3_rep2 77.23104 77.39437 70.18807 70.00662 70.34552 48.20085 48.52108
SC10_day3_rep3 77.21814 77.37906 70.34590 70.16828 70.11023 49.53767 50.52262
SC10_day8_rep1 64.53354 64.95200 72.74278 72.50885 72.28719 52.32340 53.63493
SC10_day8_rep2 64.74684 64.98592 72.82640 72.54277 72.37472 53.04326 53.43198
SC10_day8_rep3 64.45698 64.88612 72.29703 72.17124 72.00910 52.43546 53.45727
SC07_day3_rep3 SC07_day8_rep1 SC07_day8_rep2 SC07_day8_rep3 SC10_day0_rep1 SC10_day0_rep2 SC10_day0_rep3
SC01_day0_rep2
SC01_day0_rep3
SC01_day3_rep1
SC01_day3_rep2
SC01_day3_rep3
SC01_day8_rep1
SC01_day8_rep2
SC01_day8_rep3
SC07_day0_rep1
SC07_day0_rep2
SC07_day0_rep3
SC07_day3_rep1
SC07_day3_rep2
SC07_day3_rep3
SC07_day8_rep1 38.90605
SC07_day8_rep2 37.56907 23.11405
SC07_day8_rep3 37.48277 23.92987 18.61052
SC10_day0_rep1 75.55110 80.34951 82.90518 83.34079
SC10_day0_rep2 74.64518 79.77306 81.97218 82.21761 17.57404
SC10_day0_rep3 75.28027 79.43065 82.34559 82.89798 22.39705 22.72044
SC10_day3_rep1 47.74997 61.98318 61.89741 61.73654 66.19464 65.54651 66.49570
SC10_day3_rep2 47.94074 61.58165 62.33510 62.54015 65.90692 65.29904 65.89319
SC10_day3_rep3 49.87329 62.11995 63.48771 63.63671 65.62131 65.12390 66.27190
SC10_day8_rep1 52.62745 59.08464 60.79100 60.87934 67.10769 66.51370 67.46528
SC10_day8_rep2 53.02902 59.52211 60.73892 61.37117 67.23442 66.68636 67.59668
SC10_day8_rep3 52.80098 59.01285 60.61734 60.87914 66.48652 65.97014 66.65030
SC10_day3_rep1 SC10_day3_rep2 SC10_day3_rep3 SC10_day8_rep1 SC10_day8_rep2
SC01_day0_rep2
SC01_day0_rep3
SC01_day3_rep1
SC01_day3_rep2
SC01_day3_rep3
SC01_day8_rep1
SC01_day8_rep2
SC01_day8_rep3
SC07_day0_rep1
SC07_day0_rep2
SC07_day0_rep3
SC07_day3_rep1
SC07_day3_rep2
SC07_day3_rep3
SC07_day8_rep1
SC07_day8_rep2
SC07_day8_rep3
SC10_day0_rep1
SC10_day0_rep2
SC10_day0_rep3
SC10_day3_rep1
SC10_day3_rep2 18.41403
SC10_day3_rep3 25.81185 23.92363
SC10_day8_rep1 38.30838 36.56170 37.42091
SC10_day8_rep2 39.03260 37.15108 37.99369 19.07657
SC10_day8_rep3 38.30680 36.50232 37.72821 17.65951 18.41347
sampleDistMatrix <- as.matrix( sampleDists )
rownames(sampleDistMatrix) <- paste(rld2$treatment, rld2$cell, sep = " - " )
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
clustering_distance_rows = sampleDists,
clustering_distance_cols = sampleDists,
col = colors)

poisd <- PoiClaClu::PoissonDistance(t(counts(dds)))
samplePoisDistMatrix <- as.matrix( poisd$dd )
rownames(samplePoisDistMatrix) <- paste( dds$dex, dds$cell, sep=" - " )
colnames(samplePoisDistMatrix) <- NULL
pheatmap(samplePoisDistMatrix,
clustering_distance_rows = poisd$dd,
clustering_distance_cols = poisd$dd,
col = colors)

mds <- as.data.frame(colData(rld2)) %>%
cbind(cmdscale(sampleDistMatrix))
ggplot(mds, aes(x = `1`, y = `2`, color = cell, shape = treatment)) +
geom_point(size = 3) + coord_fixed() + theme_bw() +
xlab("PC1") + ylab("PC2") +
theme(legend.text = element_text(size = 10),
plot.title = element_text(size = 14,
hjust = 0.5,
face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
# legend.position = "bottom",
axis.title=element_text(size=12))

# library("genefilter")
topVarGenes <- head(order(rowVars(assay(rld2)), decreasing = TRUE), 5000)
mat <- assay(rld2)[ topVarGenes, ]
mat <- mat - rowMeans(mat)
anno <- as.data.frame(colData(rld2)[, c("cell","treatment")])
names(anno) <- c("Cell", "Treatment")
annotation_colors = list(
Cell = c(SC01="red2", SC07="green2", SC10="blue2"),
Treatment = c("0"="cyan2", "3"="darkorange", "8"="darkorchid"))
pheatmap(mat, annotation_col = anno, show_rownames = F, show_colnames = F,
annotation_colors = annotation_colors)

Time series analysis
1 DESeq2 time series analysis
# browseVignettes("rnaseqGene")
ddsTC <- DESeq(dds, test="LRT", reduced = ~ cell + treatment)
using pre-existing size factors
estimating dispersions
found already estimated dispersions, replacing these
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
resTC <- results(ddsTC)
resTC$symbol <- mcols(ddsTC)$symbol
# head(resTC[order(resTC$padj),], 4)
tc <- plotCounts(ddsTC, which.min(resTC$padj),
intgroup = c("treatment","cell"), returnData = TRUE)
ddsTC[which.min(resTC$padj),]
class: DESeqDataSet
dim: 1 27
metadata(1): version
assays(4): counts mu H cooks
rownames(1): PDK4
rowData names(35): baseMean baseVar ... deviance maxCooks
colnames(27): SC01_day0_rep1 SC01_day0_rep2 ... SC10_day8_rep2 SC10_day8_rep3
colData names(4): cell treatment group sizeFactor
ggplot(tc,
aes(x = rep(c(0,3,8), each=9), y = count, color = cell, group = cell)) +
geom_point() + geom_smooth(se = FALSE, method = "loess") + scale_y_log10() +
theme_bw() +
ggtitle("Time Course Expression of PDK4") +
labs(x="Time (days)", y="Gene Count") +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.text = element_text(size = 12),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
axis.text=element_text(size=12),
legend.title = element_text(size=12,face="bold"),
axis.title=element_text(size=12, face="bold"),
legend.position = "bottom")

resultsNames(ddsTC)
[1] "Intercept" "cell_SC07_vs_SC01" "cell_SC10_vs_SC01" "treatment_3_vs_0" "treatment_8_vs_0"
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
betas <- coef(ddsTC)
colnames(betas)
[1] "Intercept" "cell_SC07_vs_SC01" "cell_SC10_vs_SC01" "treatment_3_vs_0" "treatment_8_vs_0"
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
topGenes <- head(order(resTC$padj),50)
mat <- betas[topGenes, -c(1,2)]
thr <- 3
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
pheatmap(mat, breaks=seq(from=-thr, to=thr, length=101),
cluster_col=FALSE)

NOTE
Original code below produced many messages of
No id variables; using all as measure variables; presumably
a line for each gene. This is due to the melt function not
having any id variables to use.
Rejiggering code not yet finished. Should probably use
# 1.1 ANOVA - compare btwn sublines
# group <- as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_baseline <- list()
TukeySC07toSC01 <- list()
TukeySC10toSC01 <- list()
TukeySC10toSC07 <- list()
norm_data <- as.data.frame(assay(rld2))[c('SC01_day0_rep1',
'SC01_day0_rep2',
'SC01_day0_rep3',
'SC07_day0_rep1',
'SC07_day0_rep2',
'SC07_day0_rep3',
'SC10_day0_rep1',
'SC10_day0_rep2',
'SC10_day0_rep3')]
######################
### New code by DRT ###
######################
# samp_names <- colnames(norm_data)
# compareSubclones <- function(gene_name, dat=norm_data, samp_names=NULL, group=NULL)
# {
# if(is.null(group)) group <- as.factor(c(1,1,1,2,2,2,3,3,3))
# if(is.null(samp_names)) samp_names <- colnames(dat)
# # dfa = data for analysis
# dfa <- data.frame(value=as.numeric(t(dat[gene_name,])), group=group)
# rownames(dfa) <- samp_names
# fit <- aov(value~group, dfa)
# anova_baseline <- summary(fit)[[1]][['Pr(>F)']][1]
# results <- TukeyHSD(fit, conf.level = 0.95)
# pval <- data.frame(p_adj=results$group[,'p adj'])
# rownames(pval) <- c("TukeySC07toSC01","TukeySC10toSC01","TukeySC10toSC07")
# out <- list(anova_baseline = anova_baseline,
# pval = pval)
# # TukeySC07toSC01[gene] <- results$group[,'p adj'][1]
# # TukeySC10toSC01[gene] <- results$group[,'p adj'][2]
# # TukeySC10toSC07[gene] <- results$group[,'p adj'][3]
# return(out)
# }
# temp <- lapply(rownames(norm_data), compareSubclones)
# anova_pval <- sapply(temp, "[[", 1)
######################
### End new code ###
######################
for (gene in 1:nrow(norm_data)) {
gene_norm_data <- norm_data[gene,]
# d3 <- data.frame(y = gene_norm_data, group = group)
# fit <- lm(y~group, d3)
gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
value=as.numeric(t(gene_norm_data)))
gene_norm_data_melt$group <- group
fit <- aov(value~group, gene_norm_data_melt)
# anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
anova_baseline[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
results <- TukeyHSD(fit, conf.level = 0.95)
TukeySC07toSC01[gene] <- results$group[,'p adj'][1]
TukeySC10toSC01[gene] <- results$group[,'p adj'][2]
TukeySC10toSC07[gene] <- results$group[,'p adj'][3]
}
# print(anova_list)
anova_pval <- unlist(anova_baseline) # make array
TukeySC07toSC01_pval <- unlist(TukeySC07toSC01)
TukeySC10toSC01_pval <- unlist(TukeySC10toSC01)
TukeySC10toSC07_pval <- unlist(TukeySC10toSC07)
# Make master dataset with statistics
norm_data_stats <- as.data.frame(norm_data)
norm_data_stats <- cbind(norm_data_stats, anova_pval)
norm_data_stats <- cbind(norm_data_stats, TukeySC07toSC01_pval)
norm_data_stats <- cbind(norm_data_stats, TukeySC10toSC01_pval)
norm_data_stats <- cbind(norm_data_stats, TukeySC10toSC07_pval)
# save(norm_data_stats, file = "subcloneCounts_anova_tukey_DESeq2.RData")
# Identify genes that differ between clones based on
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_pval < sigThresh)
table(TukeySC07toSC01_pval < sigThresh)
table(TukeySC10toSC01_pval < sigThresh)
table(TukeySC10toSC07_pval < sigThresh)
sigIndecies <- which(norm_data_stats["anova_pval"] < sigThresh)
sigIndeciesAll <- which(norm_data_stats["anova_pval"] < sigThresh &
norm_data_stats["TukeySC07toSC01_pval"] < sigThresh &
norm_data_stats["TukeySC10toSC01_pval"] < sigThresh &
norm_data_stats["TukeySC10toSC07_pval"] < sigThresh)
sigDiffGenes <- rownames(norm_data_stats[sigIndecies,])
sigDiffGenesAll <- rownames(norm_data_stats[sigIndeciesAll,])
2. ANOVA btwn time points & shared btwn sublines)
group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC01 <- list()
TukeySC01_time0 <- list()
TukeySC01_time3 <- list()
TukeySC01_time8 <- list()
norm_data_SC01time <- as.data.frame(assay(rld2))[c('SC01_day0_rep1',
'SC01_day0_rep2',
'SC01_day0_rep3',
'SC01_day3_rep1',
'SC01_day3_rep2',
'SC01_day3_rep3',
'SC01_day8_rep1',
'SC01_day8_rep2',
'SC01_day8_rep3')]
for (gene in 1:nrow(norm_data_SC01time)) {
gene_norm_data <- norm_data_SC01time[gene,]
# d3 <- data.frame(y = gene_norm_data, group = group)
# fit <- lm(y~group, d3)
# gene_norm_data_melt <- melt(gene_norm_data)
gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
value=as.numeric(t(gene_norm_data)))
gene_norm_data_melt$group <- group
fit <- aov(value~group, gene_norm_data_melt)
# anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
anova_SC01[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
results <- TukeyHSD(fit, conf.level = 0.95)
TukeySC01_time0[gene] <- results$group[,'p adj'][1]
TukeySC01_time3[gene] <- results$group[,'p adj'][2]
TukeySC01_time8[gene] <- results$group[,'p adj'][3]
}
# print(anova_list)
anova_SC01_pval <- unlist(anova_SC01) # make array
TukeySC01_time0_pval <- unlist(TukeySC01_time0)
TukeySC01_time3_pval <- unlist(TukeySC01_time3)
TukeySC01_time8_pval <- unlist(TukeySC01_time8)
# Make master dataset with statistics
norm_data_stats_SC01 <- as.data.frame(norm_data_SC01time)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, anova_SC01_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time0_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time3_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time8_pval)
# save(norm_data_stats_SC01, file = "subcloneCounts_anova_tukey_DESeq2_SC01time.RData")
# Identify genes that differ between clones based on
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC01_pval < sigThresh)
table(TukeySC01_time0_pval < sigThresh)
table(TukeySC01_time3_pval < sigThresh)
table(TukeySC01_time8_pval < sigThresh)
sigIndecies_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh)
sigIndeciesAll_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh &
norm_data_stats_SC01["TukeySC01_time0_pval"] < sigThresh &
norm_data_stats_SC01["TukeySC01_time3_pval"] < sigThresh &
norm_data_stats_SC01["TukeySC01_time8_pval"] < sigThresh)
sigDiffGenes_SC01 <- rownames(norm_data_stats_SC01[sigIndecies_SC01,])
sigDiffGenesAll_SC01 <- rownames(norm_data_stats_SC01[sigIndeciesAll_SC01,])
group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC07 <- list()
TukeySC07_time0 <- list()
TukeySC07_time3 <- list()
TukeySC07_time8 <- list()
norm_data_SC07time <- as.data.frame(assay(rld2))[c('SC07_day0_rep1',
'SC07_day0_rep2',
'SC07_day0_rep3',
'SC07_day3_rep1',
'SC07_day3_rep2',
'SC07_day3_rep3',
'SC07_day8_rep1',
'SC07_day8_rep2',
'SC07_day8_rep3')]
for (gene in 1:nrow(norm_data_SC07time)) {
gene_norm_data <- norm_data_SC07time[gene,]
# d3 <- data.frame(y = gene_norm_data, group = group)
# fit <- lm(y~group, d3)
# gene_norm_data_melt <- melt(gene_norm_data)
gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
value=as.numeric(t(gene_norm_data)))
gene_norm_data_melt$group <- group
fit <- aov(value~group, gene_norm_data_melt)
# anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
anova_SC07[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
results <- TukeyHSD(fit, conf.level = 0.95)
TukeySC07_time0[gene] <- results$group[,'p adj'][1]
TukeySC07_time3[gene] <- results$group[,'p adj'][2]
TukeySC07_time8[gene] <- results$group[,'p adj'][3]
}
# print(anova_list)
anova_SC07_pval <- unlist(anova_SC07) # make array
TukeySC07_time0_pval <- unlist(TukeySC07_time0)
TukeySC07_time3_pval <- unlist(TukeySC07_time3)
TukeySC07_time8_pval <- unlist(TukeySC07_time8)
# Make master dataset with statistics
norm_data_stats_SC07 <- as.data.frame(norm_data_SC07time)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, anova_SC07_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time0_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time3_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time8_pval)
# save(norm_data_stats_SC07, file = "subcloneCounts_anova_tukey_DESeq2_SC07time.RData")
# Identify genes that differ between clones based on
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC07_pval < sigThresh)
table(TukeySC07_time0_pval < sigThresh)
table(TukeySC07_time3_pval < sigThresh)
table(TukeySC07_time8_pval < sigThresh)
sigIndecies_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh)
sigIndeciesAll_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh &
norm_data_stats_SC07["TukeySC07_time0_pval"] < sigThresh &
norm_data_stats_SC07["TukeySC07_time3_pval"] < sigThresh &
norm_data_stats_SC07["TukeySC07_time8_pval"] < sigThresh)
sigDiffGenes_SC07 <- rownames(norm_data_stats_SC07[sigIndecies_SC07,])
sigDiffGenesAll_SC07 <- rownames(norm_data_stats_SC07[sigIndeciesAll_SC07,])
3 Jack’s method
#Grab all the names from res in the DESeq matrix
topGenes <- which(res$padj <= 0.001)
countMAT <- data.frame(normalizedCounts[topGenes,])
subrl = data.frame(assay(rld2))
rlMAT = data.frame(subrl[topGenes,])
#Labeling rows with ENSG IDs
# countMAT$ensembl_gene_id = row.names(countMAT)
# countMAT$padj = res[topGenes,"padj"]
rlMAT$ensembl_gene_id = row.names(rlMAT)
rlMAT$padj = res[topGenes,"padj"]
# library(biomaRt)
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(rlMAT)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
# filters= "ensembl_gene_id",
# values=genes,
# mart=mart)
#Check if data fits a normal distribution
# plot(density(c(as.matrix(countMAT[,1:27]))))
plot(density(c(as.matrix(rlMAT[,1:27]))))
#rlMAT follows a normal distribution, therefore we will use this in the heatmap construction
#Labeling df with hgnc symbols
GE_data <- merge(G_list, rlMAT, by = "ensembl_gene_id")
#Making rownames unique hgnc symbols
rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
GE_data = GE_data[order(GE_data$padj),]
#Averaging rld between trials
Acol <- c("SC01_day0",
"SC01_day3",
"SC01_day8",
"SC07_day0",
"SC07_day3",
"SC07_day8",
"SC10_day0",
"SC10_day3",
"SC10_day8")
for(i in 1:length(Acol)){
j = 2+i
k = 2+3*i
GE_data[,Acol[i]] = rowMeans(GE_data[,c(j:k)])
}
#Calculating fold changes across conditions in a triangular matrix form
GE_mean = GE_data[,c(1,2,30:39)]
DEProc = GE_mean
startcol = 4
endcol = 12
allFC <- function(DEProc,startcol,endcol){
GE_fold = DEProc[,-c(startcol:endcol)]
colvec = colnames(DEProc)[startcol:endcol]
#Last index is a self comparison and is removed
for(k in 1:(length(colvec)-1)){
#Start with column that is 1 away from index
for(j in (k+1):length(colvec)){
compnam = paste0(colvec[j],"/",colvec[k])
#Loop through each gene/row
for(i in 1:nrow(DEProc)){
f = DEProc[i,colvec[j]]
h = DEProc[i,colvec[k]]
#Capture upregulation and down regulation
if(f>h){
GE_fold[i,compnam] = 2^(f-h)
}else{
GE_fold[i,compnam] = -2^(h-f)
}
}
}
}
return(GE_fold)
}
#Subset gene, then plot, then save plot
#Perhaps make heatmaps with scaled z scores
#Is there a way to consolidate replicate z scores? Geometric mean?
#Regular mean, then scale.
# ImpRat = colnames(GE_fold)[c(4,5,6,9,12,14,17,21,24,25,26,27,30,32,36,37,38,39)]
#Listing of all important comparisons?
ImpRat = c("SC01_day3/SC01_day0", "SC01_day8/SC01_day3", "SC01_day8/SC01_day0",
"SC07_day3/SC07_day0", "SC07_day8/SC07_day3", "SC07_day8/SC07_day0",
"SC10_day3/SC10_day0", "SC10_day8/SC10_day3", "SC10_day8/SC10_day0",
"SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0",
"SC07_day3/SC01_day3", "SC10_day3/SC01_day3", "SC10_day3/SC07_day3",
"SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8" )
Imp_fold = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", ImpRat)]
Imp_fold2 = Imp_fold[rowSums(abs(Imp_fold[,4:21])>=1.5)>=1,]
# write.table(Imp_fold,"SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t", row.names=F)
Imp_fold = read.delim("SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t")
#Subset the LF mean of important genes from Log2 Fold Change (LFC) comparison data frame.
GE_Imp = subset(GE_mean,GE_mean$ensembl_gene_id%in%Imp_fold2$ensembl_gene_id)
Necro = read.delim("KEGGNecroptosis_hsa04217_06-25-18.txt", header=T, stringsAsFactors = F)
Necro = Necro[rowSums(is.na(Necro)) == 0, ]
DE_Necro = merge(GE_Imp, Necro, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Necro) = make.names(DE_Necro[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Necro[3:29],cluster_cols = TRUE)
# write.table(DE_Necro, "KEGGNecroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)
Apop = read.delim("KEGGApoptosis_hsa04210_06-25-18.txt", header=T, stringsAsFactors = F)
Apop = Apop[rowSums(is.na(Apop)) == 0, ]
DE_Apop = merge(GE_Imp), Apop, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Apop) = make.names(DE_Apop[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Apop[3:29],cluster_cols = TRUE, scale = "row")
# write.table(DE_Apop, "KEGGApoptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)
Ferr = read.delim("KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
Ferr = Ferr[rowSums(is.na(Ferr)) == 0, ]
DE_Ferr = merge(GE_Imp, Ferr, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Ferr) = make.names(DE_Ferr[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Ferr[4:12],cluster_cols=FALSE, scale = "row")
# write.table(DE_Ferr, "KEGGFerroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)
4. Different LC comparisons. Between subclones and at baseline vs
idling.
Zscore heatmaps of relevant comparisons can be made as in above to
visualize.
#USES ABOVE CODE TO LINE 280. Run that pseudo-function.
# library(pheatmap)
#Comparisons of difEx between subclones at baseline and idling
BetweenBase = c("SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0")
BetweenIdle = c("SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8")
#Unsure of how strict to make the cutoff. Should all the genes between clones be differentially expressed (3) or is a single difference sufficient?
Btw_b = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenBase)]
Btw_b1 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=1,]
Btw_b2 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=2,]
Btw_b3 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=3,]
Btw_i = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenIdle)]
Btw_i1 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=1,]
Btw_i2 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=2,]
Btw_i3 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=3,]
#This does not account for same direction of change. This can be plotted in a heatmap to view.
#Members that were "lost" by the baseline condition at being different. Were no longer found diffEx between the subclones when comparing baseline to idling DEGs.
LostDEG_b_1 = subset(Btw_b1,!Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
LostDEG_b_2 = subset(Btw_b2,!Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
LostDEG_b_3 = subset(Btw_b3, !Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)
##Make heatmap
LostDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_3$ensembl_gene_id)
row.names(LostDEG_b_3_mean) = make.names(LostDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")
#Members that remained different.
StaticDEG_b_1 = subset(Btw_b1,Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
StaticDEG_b_2 = subset(Btw_b2,Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
StaticDEG_b_3 = subset(Btw_b3, Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)
##Some HGNC_symbols are duplicates! I switched to ensembl_gene_id to fix.
StaticDEG_i_3 = subset(Btw_i3, Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)
##Make heatmap
StaticDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%StaticDEG_b_3$ensembl_gene_id)
row.names(StaticDEG_b_3_mean) = make.names(StaticDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(StaticDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")
#Members that "gained" differences between the subclones in idling.
GainDEG_i_1 = subset(Btw_i1, !Btw_i1$ensembl_gene_id%in%Btw_b1$ensembl_gene_id)
GainDEG_i_2 = subset(Btw_i2, !Btw_i2$ensembl_gene_id%in%Btw_b2$ensembl_gene_id)
GainDEG_i_3 = subset(Btw_i3, !Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)
##Make heatmap
GainDEG_i_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%GainDEG_i_3$ensembl_gene_id)
row.names(GainDEG_i_3_mean) = make.names(GainDEG_i_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(GainDEG_i_3_mean[4:12],cluster_cols=FALSE, scale = "row")
#Members that were differentially expressed between idling (8day) and baseline within subclones. Those with shared diffEx may be convergent across multiple subclones depending on direction of expresison change.
Endpoint = c("SC01_day8/SC01_day0", "SC07_day8/SC07_day0", "SC10_day8/SC10_day0")
BtoIdle = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", Endpoint)]
BtoIdle_1 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=1,]
BtoIdle_2 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=2,]
BtoIdle_3 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=3,]
##Make heatmap
BtoIdle_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%BtoIdle_2$ensembl_gene_id)
row.names(BtoIdle_2_mean) = make.names(BtoIdle_2_mean[,"hgnc_symbol"], unique = TRUE)
BtoIdle_2_mean_incExp = BtoIdle_2_mean[which(BtoIdle_2_mean$SC01_day0 < BtoIdle_2_mean$SC01_day8),]
BtoIdle_2_mean_incExp = BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC07_day0 < BtoIdle_2_mean_incExp$SC07_day8),]
BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC10_day0 < BtoIdle_2_mean_incExp$SC10_day8),]
LostDEG_b_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_2$ensembl_gene_id)
row.names(LostDEG_b_2_mean) = make.names(LostDEG_b_2_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_2_mean[4:12],cluster_cols=FALSE, scale = "row")
BtoIdleIncExp_DEbetweenSCs = BtoIdle_2_mean_incExp[which(row.names(BtoIdle_2_mean_incExp) %in% row.names(LostDEG_b_2_mean)),]
pheatmap(BtoIdle_2_mean_incExp[4:12],cluster_cols=FALSE, scale = "row")
# library(devtools)
# # install_github("wjawaid/enrichR")
# library(enrichR)
dbs <- listEnrichrDbs()
head(dbs)
dbs <- c("GO_Molecular_Function_2015", "GO_Cellular_Component_2015", "GO_Biological_Process_2015")
enriched <- enrichr(row.names(BtoIdle_2_mean_incExp), dbs)
View(enriched[["GO_Molecular_Function_2015"]])
View(enriched[["GO_Cellular_Component_2015"]])
View(enriched[["GO_Biological_Process_2015"]])
enriched_MF_sig <- enriched[["GO_Molecular_Function_2015"]][enriched[["GO_Molecular_Function_2015"]]$Adjusted.P.value<0.05,]
enriched_MF_sig_df <- data.frame(enriched_MF_sig[,c(1,4,9)])
write.csv(enriched_MF_sig_df, "enriched_MF_significant.csv")
enriched_BP_sig <- enriched[["GO_Biological_Process_2015"]][enriched[["GO_Biological_Process_2015"]]$Adjusted.P.value<0.05,]
enriched_BP_sig_df <- data.frame(enriched_BP_sig[,c(1,4,9)])
# write.csv(enriched_BP_sig_df, "enriched_BP_significant.csv")
Gini_scGenes <- c("APOE", "BCAN", "CES1", "CITED1",
"CPM", "CTSF", "DCT", "EDNRB",
"EGR1", "ESRP1", "FSTL1", "MALAT1",
"MAP2K6", "MCF2L", "MLANA", "MXD4",
"OCA2", "PMEL", "SEMA6A", "SNAI2",
"SOX4", "TSPAN10")
enriched_sc <- enrichr(Gini_scGenes, dbs)
row.names(BtoIdle_2_mean_incExp) %in% Gini_scGenes
Rest of Jack’s Analysis
#Visually inspect trending members from heatmaps.
#Plots of specific trending members?
p <- ggplot(data=df2, aes(x=dose, y=len, fill=supp)) +
geom_bar(stat="identity", color="black", position=position_dodge())+
theme_minimal()
NOTE: code below reuses object names… WILL OVERWRITE!
#GLM Coef Heatmap.
betas <- coef(dds)
topGenes <- which(res$padj <= 0.001)
mat <- data.frame(betas[topGenes,])
mat$ensembl_gene_id = row.names(mat)
mat$padj = res[topGenes,"padj"]
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(mat)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
# filters= "ensembl_gene_id",
# values=genes,
# mart=mart)
# GE_data <- merge(mat, G_list, by = "ensembl_gene_id")
# rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
# GE_data = GE_data[order(GE_data$padj),]
#Sorting script to pick out entries greater than or less than +-1
eg = c()
for(i in 3:10){
g = which(GE_data[,i] > 3 | GE_data[,i] < -3)
eg = c(eg,g)
}
eg = unique(eg)
mat = GE_data[eg,-c(1:2,11,12)]
thr <- 3
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
# library(pheatmap)
pheatmap(mat, cluster_cols = FALSE)
# ssdg = sdg[1:1000, ]
dim(sdg)
head(sdg)
LS0tCnRpdGxlOiAiUk5Bc2VxIERFU2VxMiBUaW1lIENvdXJzZSIKYXV0aG9yOiAiQ29yZXkgSGF5Zm9yZCAobW9kaWZlZCBmcm9tIEphY2spIgpkYXRlOiAiTm92ZW1iZXIgMjAxOC1KYW51YXJ5IDIwMTkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBEUlQgbW9kaWZpZWQgZmlsZSB0byB3b3JrIG9uIGFueSBjb21wdXRlcgoyMDIyLTAxLTE3ClNvbWUgZmlsZXMgYXBwZWFyZWQgdG8gYmUgbWlzc2luZyB0byBydW4gdGhpcyBub3RlYm9vay4gVHJ5aW5nIHRvIGdldCBhbGwgdG8gcnVuCgojIyBPdmVydmlldwpUaGlzIGlzIGEgcGlwZWxpbmUgZm9yIGRpZmZlcmVudGlhbCBhbmFseXNpcyBvZiBSTkFTZXEgZGF0YSBmcm9tIFNLTUVMNSBzdWJsaW5lcyB1c2luZyBERVNlcTIgc3RhdGlzdGljYWwgcGFja2FnZS4gVGhyZWUgc3VibGluZXM6IFNDMDEgKCpyZWdyZXNzaW5nKiksIFNDMDcgKCpzdGF0aW9uYXJ5KikgYW5kIFNDMTAgKCpleHBhbmRpbmcqKSB3ZXJlIGFuYWx5emVkIGZvciBnZW5lIGV4cHJlc3Npb24gZGlmZmVyZW5jZXMuIEluIGFkZGl0aW9uLCB0aW1lIGNvdXJzZSBjaGFuZ2VzIGluIDh1TSBQTFg0NzIwIHdlcmUgYWxzbyBwZXJmb3JtZWQgZm9yIGVhY2ggc3VibGluZS4gVGltZSBwb2ludHMgYXJlOiAwLCAzZCwgOGQuIFRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgd2lsbCBiZSBwZXJmb3JtZWQgYmFzZWQgb24gdGhlIGNvbnRyYXN0cyBkZWZpbmVkIGJlbG93LiAKR2VuZXJhbCBzdGVwcyBmb3IgdGhlIGFuYWx5c2lzIGFyZToKICAKIyMjMS4gUmVhZCBjb3VudHMgdGFibGU6IAorIENvdWxkIGJlIHJlYWQgZGlyZWN0bHkgYXMgYSBjc3YvdHh0IGZpbGUuIAorIEFsaWdubWVudCBhbmQgcmVhZCBjb3VudHMgY291bGQgYmUgZG9uZSB3aXRoaW4gUiBlbnZpcm9ubWVudCB0byBjcmVhdGUgcmVhZCBjb3VudHMgdGFibGUuIAoxLiBEZWZpbmUgd29ya2luZyBkaXJlY3RvcnksIGxvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcy4gCjIuIEdldCByZWFkIGNvdW50cyB0YWJsZS4gClJlYWQgdGhlIHJhdyBjb3VudHMgZmlsZSBwcm9jZXNzZWQgYnkgZmVhdHVyZUNvdW50cy4gVGhlIGZhc3RxIGZpbGVzIHdlcmUgYWxpZ25lZCB3aXRoIEhpU2F0MiwgYW5kIHRoZSByZWFkIGNvdW50cyB3ZXJlIG9idGFpbmVkIHVzaW5nIGZlYXR1cmVDb3VudHMgb2YgUnN1YnJlYWQgcGFja2FnZXMuCgpgYGB7ciBJbnN0YWxsYXRpb24sIGV2YWw9RkFMU0V9CnBrZ3MgPC0gYygiQmlvY01hbmFnZXIiLCJERVNlcTIiLCJvcmcuSHMuZWcuZGIiLCJjbHVzdGVyUHJvZmlsZXIiLCJIRE8uZGIiLAogICAgICAgICAgInBoZWF0bWFwIiwiZ2duZXdzY2FsZSIsIlBvaUNsYUNsdSIsImVucmljaFIiLCJndGFibGUiLCJSbWlzYyIpCnNvdXJjZSgiZ2V0UmVxZFBrZ3MuciIpCmdldFJlcWRQa2dzKHBrZ3MpCmBgYAoKCmBgYHtyIFNldHVwfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoZXhwcj17CiAgICBsaWJyYXJ5KHBseXIpCiAgICBsaWJyYXJ5KGRwbHlyKQogICAgbGlicmFyeShnZ3Bsb3QyKQogICAgbGlicmFyeShnZ25ld3NjYWxlKQogICAgbGlicmFyeShyZXNoYXBlMikKICAgIGxpYnJhcnkoREVTZXEyKQogICAgbGlicmFyeShnZ3JlcGVsKQogICAgbGlicmFyeShwaGVhdG1hcCkKICAgIGxpYnJhcnkob3JnLkhzLmVnLmRiKQogICAgbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCiAgICBsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKQogICAgbGlicmFyeShlbnJpY2hSKQogICAgbGlicmFyeShiaW9tYVJ0KQogICAgbGlicmFyeShSbWlzYykKfSkKClNBVkVGSUxFUyA8LSBGQUxTRQpgYGAKCmBgYHtyLCBlY2hvPVRSVUV9CmQgPC0gcmVhZC5jc3YoIi4uL2RhdGEvZmVhdHVyZUNvdW50c19tYXRyaXhfYWxsLmNzdiIsIGhlYWRlcj1ULCBzZXA9IiwiKQoKI1JlbmFtZSBjb2x1bW5zCmNvbHMgPC0gYygiZW5zZW1ibF9nZW5lX2lkIiwgIlNDMDFfZGF5MF9yZXAxIiwgIlNDMDFfZGF5MF9yZXAyIiwgIlNDMDFfZGF5MF9yZXAzIiwKICAgICAgICAgICJTQzAxX2RheTNfcmVwMSIsICJTQzAxX2RheTNfcmVwMiIsICJTQzAxX2RheTNfcmVwMyIsCiAgICAgICAgICAiU0MwMV9kYXk4X3JlcDEiLCAiU0MwMV9kYXk4X3JlcDIiLCAiU0MwMV9kYXk4X3JlcDMiLAogICAgICAgICAgIlNDMDdfZGF5MF9yZXAxIiwgIlNDMDdfZGF5MF9yZXAyIiwgIlNDMDdfZGF5MF9yZXAzIiwKICAgICAgICAgICJTQzA3X2RheTNfcmVwMSIsICJTQzA3X2RheTNfcmVwMiIsICJTQzA3X2RheTNfcmVwMyIsCiAgICAgICAgICAiU0MwN19kYXk4X3JlcDEiLCAiU0MwN19kYXk4X3JlcDIiLCAiU0MwN19kYXk4X3JlcDMiLAogICAgICAgICAgIlNDMTBfZGF5MF9yZXAxIiwgIlNDMTBfZGF5MF9yZXAyIiwgIlNDMTBfZGF5MF9yZXAzIiwKICAgICAgICAgICJTQzEwX2RheTNfcmVwMSIsICJTQzEwX2RheTNfcmVwMiIsICJTQzEwX2RheTNfcmVwMyIsCiAgICAgICAgICAiU0MxMF9kYXk4X3JlcDEiLCAiU0MxMF9kYXk4X3JlcDIiLCAiU0MxMF9kYXk4X3JlcDMiKQpuYW1lcyhkKSA8LSBjb2xzCmVuc2VtYmwgPC0gdXNlRW5zZW1ibCgiZW5zZW1ibCIsIG1pcnJvcj0idXNlYXN0IikKbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKZ2VuZXMgPC0gZCRlbnNlbWJsX2dlbmVfaWQKR19saXN0IDwtIGdldEJNKGF0dHJpYnV0ZXM9IGMoImVuc2VtYmxfZ2VuZV9pZCIsImhnbmNfc3ltYm9sIiksCiAgICAgICAgICAgICAgICBmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwKICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKICAgICAgICAgICAgICAgIG1hcnQ9bWFydCkKCkdFX2RhdGEgPC0gbWVyZ2UoZCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQpkIDwtIEdFX2RhdGFbLCAtMV0KZCA8LSBkW2MoMjgsIHNlcSgxOjI3KSldCnJvd25hbWVzKGQpIDwtIG1ha2UubmFtZXMoZCRoZ25jX3N5bWJvbCwgdW5pcXVlID0gVCkKZCA8LSBkWywgMjoyOF0KCiMgcmVtb3ZlIGdlbmVzIHdpdGggPDUgY291bnRzIGluIGFsbCBzYW1wbGVzCmQgPC0gZFthcHBseShkLCAxLCBmdW5jdGlvbih4KSBhbGwoeCA+IDUpKSxdCgoKY291bnRkYXRhIDwtIGQKIyBiYXNlbGluZSA8LSBjKDEsMiwzLDEwLDExLDEyLDE5LDIwLDIxKQojIHRyZWF0M2QgIDwtIGMoNCw1LDYsMTMsMTQsMTUsMjIsMjMsMjQpCiMgdHJlYXQ4ZCAgPC0gYyg3LDgsOSwxNiwxNywxOCwyNSwyNiwyNykKIyAjIGRlZmluZSB0aGUgZ3JvdXBzIGJ5IHN1YmNsb25lcwojIHNjMDEgPC0gYyhiYXNlbGluZVsxOjNdLCB0cmVhdDNkWzE6M10sIHRyZWF0OGRbMTozXSkKIyBzYzA3IDwtIGMoYmFzZWxpbmVbNDo2XSwgdHJlYXQzZFs0OjZdLCB0cmVhdDhkWzQ6Nl0pCiMgc2MxMCA8LSBjKGJhc2VsaW5lWzc6OV0sIHRyZWF0M2RbNzo5XSwgdHJlYXQ4ZFs3OjldKQojICMgR2V0IHRoZSBjb3VudGRhdGEgc3BlY2lmaWMgdG8gY29uZGl0aW9uczogCiMgIyBjb3VudGRhdGEgPC0gY291bnRkYXRhWyxjKGJhc2VsaW5lKV0gCiMgcm93bmFtZXMoY291bnRkYXRhKSA8LSBkWywiZW5zZW1ibF9nZW5lX2lkIl0KaGVhZChjb3VudGRhdGEpCmBgYAoKIyMjIElkZW50aWZ5aW5nIGRpZmZlcmVudCBpb24gY2hhbm5lbCBnZW5lIGxpc3RzCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmNvdW50ZGF0YV9oaXN0YW1pbmUgPSBjb3VudGRhdGFbYygiSFJIMSIsIkhSSDIiLCJIUkgzIiwiSFJINCIpLF0KY291bnRkYXRhX0lQcyA9IGNvdW50ZGF0YVtjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpLF0KY291bnRkYXRhX0lQcmVsID0gY291bnRkYXRhW2MoIklUUFIxIiwgIklUUFIyIiwgIklUUFIzIiwgIlBMQ0cxIiwiREdLQSIsICJER0tCIiwgIkRHS0QiLCAiREdLRSIsICJER0tHIiwgIkRHS0giLCAiREdLSSIsICJER0tLIiwgIkRHS1EiLCAiREdLWiIsICJQSUszQ0EiLCAiUElLM0NCIiwgIlBJSzNDRCIsICJQSUszQ0ciLCAiUElLM0MyQSIsICJQSUszQzJCIiwgIlBJSzNDMkciLCAiUElLM0MzIiksXQpjb3VudGRhdGFfbXVzYyA9IGNvdW50ZGF0YVtjKCJDSFJNMSIsICJDSFJNMiIsICJDSFJNMyIsICJDSFJNNCIsICJDSFJNNSIpLF0KY291bnRkYXRhX2dsdXQgPSBjb3VudGRhdGFbYygiR1JNMSIsICJHUk0yIiwgIkdSTTMiLCAiR1JNNCIsICJHUk01IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JNNiIsICJHUk03IiwgIkdSTTgiLCAiR1JJQTEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JJQTIiLCAiR1JJQTMiLCAiR1JJQTQiLCAiR1JJRDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklEMiIsICJHUklLMSIsICJHUklLMiIsICJHUklLMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklLNCIsICJHUklLNSIsICJHUklOMSIsICJHUklOMkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOMkIiLCAiR1JJTjJDIiwgIkdSSU4yRCIsICJHUklOM0EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOM0IiKSxdCgojIyMgUGxvdCBJUCMgZGF0YQojIHNvdXJjZSgic3VtbWFyeVNFLlIiKQpJUHNfbWF0Y2ggPSBjb2xzcGxpdChjb2xuYW1lcyhjb3VudGRhdGFfSVBzKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCklQc19wbG90ID0gY2JpbmQoSVBzX21hdGNoLCB0KGNvdW50ZGF0YV9JUHMpKQpJUHNfbWVsdCA9IG1lbHQoZGF0YSA9IElQc19wbG90LCBpZC52YXJzID0gYygiUG9wdWxhdGlvbiIsICJUaW1lIiwgIlJlcGxpY2F0ZSIpLCBtZWFzdXJlLnZhcnMgPSBjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpKQpJUHNfZGF0ID0gUm1pc2M6OnN1bW1hcnlTRShJUHNfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpJUHNfZGF0JFRpbWUgPSBhcy5udW1lcmljKGdzdWIoIlteWzpkaWdpdDpdXSIsIiIsSVBzX2RhdCRUaW1lKSkKSVBzX2RhdF9zdWIgPSBzdWJzZXQoSVBzX2RhdCwgdmFyaWFibGUgJWluJSBjKCJJVFBSMiIsIklUUFIzIikpCklQc19nZ3Bsb3RlZCA8LSBnZ3Bsb3QoSVBzX2RhdF9zdWIsIGFlcyh4PVRpbWUsIHk9dmFsdWUsIGdyb3VwID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFBvcHVsYXRpb24pKSkgKyBnZW9tX2xpbmUoc2l6ZT0xLjUsIGFlcyhjb2xvciA9IFBvcHVsYXRpb24sIGxpbmV0eXBlID0gdmFyaWFibGUpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsKZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj12YWx1ZS1zZCwgeW1heD12YWx1ZStzZCwgY29sb3IgPSBQb3B1bGF0aW9uKSwgd2lkdGg9LjIsIHNpemU9MS41KSArCnRoZW1lX2J3KCkgKyB4bGFiKCJUaW1lIChkYXlzKSIpICsgeWxhYigiR2VuZSBDb3VudHMiKSArCmdndGl0bGUoIklQMyBnZW5lIHJlY2VwdG9ycyIpICsKdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpLAogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSkKCklQc19nZ3Bsb3RlZAojIGdnc2F2ZSgiSVAzUjJfM19UaW1lU2VyaWVzUk5Bc2VxX2Nsb25lcy5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCmBgYAoKIyMjMi4gQ29udmVydCBjb3VudHMgdGFibGUgdG8gREVTZXEyIG9iamVjdC4gCkNvbnZlcnQgY291bnRzIHRhYmxlIHRvIG9iamVjdCBmb3IgREVTZXEyIG9yIGFueSBvdGhlciBhbmFseXNpcyBwaXBlbGluZS4gVGhpcyBzdGVwIHdpbGwgcmVxdWlyZSB0byBwcmVwYXJlIGRhdGEgb2JqZWN0IGluIGEgZm9ybSB0aGF0IGlzIHN1aXRhYmxlIGZvciBhbmFseXNpcyBpbiBERVNlcTIgcGlwZWxpbmU6IHdlIHdpbGwgbmVlZCB0aGUgZm9sbG93aW5nIHRvIHByb2NlZWQ6CiAgCiAgKyBjb3VudGRhdGE6IGEgdGFibGUgd2l0aCB0aGUgcmVhZC9mcmFnbWVudCBjb3VudHMuIAorIGNvbGRhdGE6IGEgdGFibGUgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlcy4gCgpVc2luZyB0aGUgbWF0cml4IG9mIGNvdW50cyBhbmQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZSwgd2UgbmVlZCB0byBjb25zdHJ1Y3QgdGhlIERFU2VxRGF0YVNldCBvYmplY3QsIGZvciB3aGljaCB3ZSB3aWxsIHVzZSBERVNlcURhdGFTZXRGcm9tTWF0cml4Li4uLi4KCiMjIyMgMS4gRGVmaW5lIHRoZSBzYW1wbGVzIGFuZCB0cmVhdG1lbnQgY29uZGl0aW9ucy4gCmBgYHtyfQpjb25kaXRpb24gPC0gYygiMCIsICIzIiwgIjgiKQp0cmVhdG1lbnQgPC0gcmVwKGNvbmRpdGlvbiwgZWFjaD0zKSAjIFRocmVlIGJpb2xvZ2ljYWwgcmVwbGljYXRlcwp1bmlxdWUodHJlYXRtZW50KQpjZWxsIDwtIGMoIlNDMDEiLCAiU0MwNyIsIlNDMTAiKSAjc3VibGluZXMgdXNlZCBmb3IgdGhlIGFuYWx5c2lzCmNlbGxOYW1lIDwtIHJlcChjZWxsLCBlYWNoPTMpCgpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoY2VsbD1yZXAoY2VsbE5hbWUpLCB0cmVhdG1lbnQ9cmVwKHRyZWF0bWVudCwgZWFjaD0zKSkKZ3JvdXAgPSBmYWN0b3IocGFzdGUoY29sZGF0YSRjZWxsLCBjb2xkYXRhJHRyZWF0bWVudCwgc2VwPSIuIikpCmNvbGRhdGEkZ3JvdXAgPSBncm91cApgYGAKCiMjIyMgMi4gY29uc3RydWN0IHRoZSBERVNlcURhdGFTZXQgb2JqZWN0IGZyb20gdGhlIG1hdHJpeCBvZiBjb3VudHMgYW5kIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gdGFibGUuIApEZXNjcmliZWQgYWJvdmUgYXJlOiBjb3VudGRhdGEtIHJhdyBjb3VudHMsIGNvbGRhdGE6IHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZS4gCmBgYHtyfQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2xkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGNlbGwgKyB0cmVhdG1lbnQgKyBjZWxsOnRyZWF0bWVudCkKZGRzCmBgYAoKIyMjMy4gRXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uClRoZXJlIGFyZSB0d28gc2VwYXJhdGUgc3RlcHMgaW4gdGhlIHdvcmtmbG93OyB0aGUgb25lIHdoaWNoIGludm9sdmVzIGRhdGEgdHJhbnNmb3JtYXRpb25zIGluIG9yZGVyIHRvIHZpc3VhbGl6ZSBzYW1wbGUgcmVsYXRpb25zaGlwcyBhbmQgdGhlIHNlY29uZCBzdGVwIGludm9sdmVzIHN0YXRpc3RpY2FsIHRlc3RpbmcgbWV0aG9kcyB3aGljaCByZXF1aXJlcyB0aGUgb3JpZ2luYWwgcmF3IGNvdW50cy4gCgojIyMjIDEuIFByZS1maWx0ZXJpbmcgYW5kIG5vcm1hbGl6YXRpb24uIApQcmUtZmlsdGVyaW5nIGFuZCBub3JtYWxpemF0aW9uIGlzIHJlcXVpcmVkIHRvIHJlbW92ZSBsb3dseSBleHByZXNzZWQgZ2VuZXMuIAoKYGBge3J9CmRkczIgPC0gZGRzW3Jvd1N1bXMoY291bnRzKGRkcykpID4gMTgsIF0gIyByZW1vdmUgcm93cyB3aXRoIG1pbmltdW0gb2YgMiByZWFkIHBlciBjb25kaXRpb24KCm5yb3coZGRzMikKIyBzYXZlKGRkczIsIGZpbGUgPSAiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCiMgbG9hZCgiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCgpgYGAKCiMjIyMgMi4gVmlzdWFsaXplIHNhbXBsZS10by1zYW1wbGUgZGlzdGFuY2VzLiAKV2UgY291bGQgdXNlIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgdG8gdmlzdWFsaXplIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzLiAKYGBge3J9CnJsZCA8LSBybG9nKGRkczIsIGJsaW5kID0gRkFMU0UpCiMgc2F2ZShybGQsIGZpbGUgPSAiUkxEX1NDLTEsNywxMF8wLDMsOGRfMjAxODA3MDEuUkRhdGEiKQojIGxvYWQoIlJMRF9TQy0xLDcsMTBfMCwzLDhkXzIwMTgwNzAxLlJEYXRhIikKcGxvdFBDQShybGQsIGludGdyb3VwID0gYygiY2VsbCIsICJ0cmVhdG1lbnQiKSwgbnRvcD01MDAwKQoKIyMgVXNlIHByY29tcCBmdW5jdGlvbgojIENvbG9yZWQgYnkgY2VsbCBsaW5lLCBzaGFwZSBieSB0aW1lIHBvaW50LCBsaW5lcyBjb25uZWN0aW5nIHRpbWUKcGNhX0RFc2VxIDwtIHByY29tcCh0KGFzc2F5KHJsZCkpKQpwY2FfREVzZXFfcGVyYyA8LSByb3VuZCgxMDAqcGNhX0RFc2VxJHNkZXZeMi9zdW0ocGNhX0RFc2VxJHNkZXZeMiksMSkKcGNhX0RFc2VxX2RmIDwtIGRhdGEuZnJhbWUoUEMxID0gcGNhX0RFc2VxJHhbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgUEMyID0gcGNhX0RFc2VxJHhbLDJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlID0gY29sbmFtZXMoYXNzYXkocmxkKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGwubGluZSA9IHJlcChjKCJTQzAxIiwgIlNDMDciLCAiU0MxMCIpLCBlYWNoID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRheSA9IHJlcChjKCJEYXkwIiwgIkRheTMiLCAiRGF5OCIpLCBlYWNoID0gMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxpY2F0ZSA9IHJlcChjKCJSZXAxIiwgIlJlcDIiLCAiUmVwMyIpLCB0aW1lcz05KSkKCnBjYV9ERXNlcV9tZWFucyA8LSBkZHBseShwY2FfREVzZXFfZGYsIC4oY2VsbC5saW5lLCBkYXkpLCBzdW1tYXJpc2UsIG1lYW5QQzEgPSBtZWFuKFBDMSksIG1lYW5QQzIgPSBtZWFuKFBDMikpCgpnZ3Bsb3QocGNhX0RFc2VxX2RmLCBhZXMoUEMxLFBDMiwgY29sb3IgPSBjZWxsLmxpbmUpKSsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGRheSksIHNpemU9NSkgKwogIGdlb21fcGF0aChkYXRhID0gcGNhX0RFc2VxX21lYW5zLCAKICAgICAgICAgICAgYWVzKHg9bWVhblBDMSwgeT1tZWFuUEMyLAogICAgICAgICAgICAgICAgY29sb3I9Y2VsbC5saW5lKSwgYXJyb3cgPSBhcnJvdygpLAogICAgICAgICAgICBzaXplID0gMikgKwogIGxhYnMoeD1wYXN0ZTAoIlBDMSAoIixwY2FfREVzZXFfcGVyY1sxXSwiJSB2YXJpYW5jZSkiKSwgeT1wYXN0ZTAoIlBDMiAoIixwY2FfREVzZXFfcGVyY1syXSwiJSB2YXJpYW5jZSkiKSkgKwogIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQQ0EgLSBTdWJjbG9uZXMgaW4gVGltZSIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQogIAoKCmBgYAoKIyMjIDQuIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzLiAKQWx3YXlzIG1ha2Ugc3VyZSB0byB1c2UgdGhlIHVubm9ybWFsaXplZCByYXcgY291bnRzIGZvciB0aGlzLiBXZSB3aWxsIHVzZSBERVNlcSBmdW5jdGlvbiB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBhbmFseXNpcyBiZXR3ZWVuIHNhbXBsZXM7IFVubGVzcyBzcGVjaWZpZWQsIHRoZSBhbmFseXNpcyBpcyBiZXR3ZWVuIHRoZSBsYXN0IGdyb3VwIGFuZCB0aGUgZmlyc3QgZ3JvdXAuIERpZmZlcmVudCBjb21wYXJpc29uIGNhbiBiZSBkb25lIHVzaW5nICdjb250cmFzdCcgYXJndW1lbnQuIFN0ZXBzIGludm9sdmVkIHVuZGVybmVhdGg6CiAgCiAgMS4gZXN0aW1hdGlvbiBvZiBzaXplIGZhY3RvcnMgKGNvbnRyb2xzIGZvciBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoIG9mIHRoZSBzYW1wbGVzKQoyLiBlc3RpbWF0aW9uIG9mIGRpc3BlcnNpb24gdmFsdWVzIGZvciBlYWNoIGdlbmUsCjMuIGZpdHRpbmcgYSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwKCiMjIyMgMS4gUnVubmluZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcGlwZWxpbmUuIApgYGB7ciwgY2FjaGU9VFJVRX0KZGVzaWduKGRkczIpID0gfiBjZWxsICsgdHJlYXRtZW50ICsgY2VsbDp0cmVhdG1lbnQKZGRzIDwtIERFU2VxKGRkczIsIHRlc3QgPSAiTFJUIiwgcmVkdWNlZCA9IH4gY2VsbCArIHRyZWF0bWVudCkKIyBzYXZlKGRkcywgZmlsZSA9ICJERVNlcV9TQzEsNywxMF9UaW1lY291cnNlX0xSVC5SRGF0YSIpCiMgbG9hZCgiREVTZXFfU0MxLDcsMTBfVGltZWNvdXJzZV9MUlQuUkRhdGEiKQojIGRkcwpgYGAKCiMjIyMgMi4gQnVpbGRpbmcgdGhlIHJlc3VsdHMgdGFibGUuIApCeSBkZWZhdWx0LCByZXN1bHRzIHdpbGwgZXh0cmFjdCB0aGUgZXN0aW1hdGVkIGxvZzIgZm9sZCBjaGFuZ2VzIGFuZCBwIHZhbHVlcyBmb3IgdGhlIGxhc3QgdmFyaWFibGUgaW4gdGhlIGRlc2lnbiBmb3JtdWxhLiBJZiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgbGV2ZWxzIGZvciB0aGlzIHZhcmlhYmxlLCByZXN1bHRzIHdpbGwgZXh0cmFjdCB0aGUgcmVzdWx0cyB0YWJsZSBmb3IgYSBjb21wYXJpc29uIG9mIHRoZSBsYXN0IGxldmVsIG92ZXIgdGhlIGZpcnN0IGxldmVsLiAKYGBge3J9CiMgRXNpbWF0ZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBncm91cHMgYnk6ICMgYSkgTG93ZXJpbmcgdGhlIEZEUiAocGFkaikgb3IgKGIpIHJhaXNlIHRoZSBsb2cyIGZvbGQgY2hhbmdlLgoKcmVzdWx0c05hbWVzKGRkcykKIyBhbHBoYSA9IEZEUiBhZGp1c3RlZCBwIHZhbHVlIGN1dG9mZgpyZXMgPC0gcmVzdWx0cyhkZHMsIGFscGhhID0gMC4wMDEpCnN1bW1hcnkocmVzKQpyZXNPcmRlcmVkIDwtIHJlc1tvcmRlcihyZXMkcHZhbHVlKSxdCnJkYXRhID0gYXMuZGF0YS5mcmFtZShyZXMpCmBgYAojIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb246IGRheXMgMCB0byA4CkNoYW5nZSBzaWduaWZpY2FudCBsb2cyIGZvbGQgY2hhbmdlIHRvIDEuNTg1ICg9PSAzLWZvbGQgY2hhbmdlIGluIGxvZzIgc3BhY2UpLgpgYGB7cn0KcmVzXzB0bzhkIDwtIHJlc3VsdHMoZGRzLCBuYW1lPSJ0cmVhdG1lbnRfOF92c18wIiwgY29va3NDdXRvZmYgPSAwLjk5LCAKICAgICAgICAgICAgICAgICAgICAgaW5kZXBlbmRlbnRGaWx0ZXJpbmcgPSBUUlVFLCBhbHBoYSA9IDAuMDUsIHBBZGp1c3RNZXRob2QgPSAiQkgiKQpzdW1tYXJ5KHJlc18wdG84ZCkKIyBvcmRlciByZXN1bHRzIHRhYmxlIGJ5IHRoZSBzbWFsbGVzdCBhZGp1c3RlZCBwIHZhbHVlOgpyZXNfMHRvOGQgPC0gcmVzXzB0bzhkW29yZGVyKHJlc18wdG84ZCRwYWRqKSxdCnJlc3VsdHNfMHRvOGQgPC0gYXMuZGF0YS5mcmFtZShyZXNfMHRvOGQpCgpyZXN1bHRzXzB0bzhkIDwtIG11dGF0ZShyZXN1bHRzXzB0bzhkLCBzaWc9aWZlbHNlKHJlc3VsdHNfMHRvOGQkcGFkajwwLjA1ICYgcmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSA+IDEuNTg1LCAiVXByZWd1bGF0ZWQiLCBpZmVsc2UocmVzdWx0c18wdG84ZCRwYWRqPDAuMDUgJiByZXN1bHRzXzB0bzhkJGxvZzJGb2xkQ2hhbmdlIDwgLTEuNTg1LCAiRG93bnJlZ3VsYXRlZCIsICJOb3QgU2lnbmlmaWNhbnQiKSkpCgpyb3cubmFtZXMocmVzdWx0c18wdG84ZCkgPC0gcm93Lm5hbWVzKHJlc18wdG84ZCkKCgpoZWFkKHJlc3VsdHNfMHRvOGQpCkRFZ2VuZXNfMHRvOGQgPC0gcmVzdWx0c18wdG84ZFt3aGljaChhYnMocmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSkgPiBsb2cyKDEuNSkgJiByZXN1bHRzXzB0bzhkJHBhZGogPCAwLjA1KSxdCgppZihTQVZFRklMRVMpIHdyaXRlLmNzdihERWdlbmVzXzB0bzhkLCBmaWxlPSJ+L0Rlc2t0b3AvREVnZW5lc18wdG84ZC5jc3YiKQpgYGAKCiMjIyBWb2xjYW5vIHBsb3QKYGBge3J9CnZvbGNhbm8gPC0gZ2dwbG90KHJlc3VsdHNfMHRvOGQsIGFlcyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sID0gc2lnKSkgKyB0aGVtZV9idygpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImdyZXkiLCAiZ3JlZW4zIikpICsKICAjIGdndGl0bGUoIlZvbGNhbm8gUGxvdCBvZiBVbnRyZWF0ZWQgdnMgSWRsaW5nIikgKwogIGxhYnMoeD0ibG9nMihGb2xkIENoYW5nZSkiLCB5PSJMb2coT2RkcyBSYXRpbykiKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnZvbGNhbm8KIyB2b2xjYW5vICsgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9cmVzdWx0c18wdG84ZFsxOjEwLCBdLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlcyhsYWJlbD1yb3duYW1lcyhyZXN1bHRzXzB0bzhkWzE6MTAsIF0pKSkKIyBzYXZlKHJlc3VsdHNfMHRvOGQsIGZpbGU9InVudHJlYXRlZElkbGluZ19ERUEuUkRhdGEiKQpgYGAKCgpgYGB7cn0KREVnZW5lc18wdG84ZCA8LSBERWdlbmVzXzB0bzhkW29yZGVyKGFicyhERWdlbmVzXzB0bzhkJGxvZzJGb2xkQ2hhbmdlKSxERWdlbmVzXzB0bzhkJHNpZywgZGVjcmVhc2luZyA9IFRSVUUpLF0KdGVtcCA8LSBERWdlbmVzXzB0bzhkW0RFZ2VuZXNfMHRvOGQkYmFzZU1lYW4gPiAzMDAsXQp0ZW1wIDwtIHRlbXBbYWJzKHRlbXAkbG9nMkZvbGRDaGFuZ2UpPjIsXQppZihTQVZFRklMRVMpIHdyaXRlLmNzdihERWdlbmVzXzB0bzhkLCBmaWxlID0gIkRFZ2VuZXNfMHRvOGQuY3N2IikKYGBgCgoKIyBHZW5lcmF0aW5nIElvbiBDaGFubmVsIFNwZWNpZmljIEdlbmUgRGF0YWZyYW1lcwpgYGB7cn0KdGVzdCA8LSBhc3NheShkZHMpCnR5cGVzIDwtIGMoIkFUUCIsICJUUlAiLCAiR0FCUiIsICJDUkFDUiIsICJTTEMiLCAiS0NOIiwgIkNBQ04iLCAiR1JJIiwgIkFCQyIsICJTQ04iLCAiVFJQIiwgIlJJQzMiLCAiQ0hSTkQiLCAiUllSIikKc2FtcGxlcyA8LSBjKCJTQzAxX2RheTAiLCAiU0MwMV9kYXkzIiwgIlNDMDFfZGF5OCIsICJTQzA3X2RheTAiLCAiU0MwN19kYXkzIiwgIlNDMDdfZGF5OCIsICJTQzEwX2RheTAiLCAiU0MxMF9kYXkzIiwgIlNDMTBfZGF5OCIpCnRlc3QgPC0gdGVzdFtncmVwKHBhc3RlKHR5cGVzLCBjb2xsYXBzZT0ifCIpLCByb3duYW1lcyh0ZXN0KSksXQp0ZXN0MSA8LSBzYXBwbHkoc2FtcGxlcywgZnVuY3Rpb24oeCkgcm93TWVhbnModGVzdFssIGdyZXAoeCwgY29sbmFtZXModGVzdCkpXSkpCnJvd25hbWVzKHRlc3QxKSA8LSByb3duYW1lcyh0ZXN0KQp0ZXN0MSA8LSB0ZXN0MVtvcmRlcihyb3duYW1lcyh0ZXN0MSkpLF0KdGVzdDIgPC0gYXMuZGF0YS5mcmFtZSh0ZXN0MSkKdGVzdDJbImwyRkNfU0MwMV8wdG84Il0gPC0gbG9nMih0ZXN0MlsiU0MwMV9kYXk4Il0vdGVzdDJbIlNDMDFfZGF5MCJdKQp0ZXN0MlsibDJGQ19TQzA3XzB0bzgiXSA8LSBsb2cyKHRlc3QyWyJTQzA3X2RheTgiXS90ZXN0MlsiU0MwN19kYXkwIl0pCnRlc3QyWyJsMkZDX1NDMTBfMHRvOCJdIDwtIGxvZzIodGVzdDJbIlNDMTBfZGF5OCJdL3Rlc3QyWyJTQzEwX2RheTAiXSkKdGVzdDMgPC0gc3Vic2V0KHRlc3QyLCBsMkZDX1NDMDFfMHRvOCA+IDEgJiBsMkZDX1NDMDdfMHRvOCA+IDEgJiBsMkZDX1NDMTBfMHRvOCA+IDEpCnRlc3Q0IDwtIHN1YnNldCh0ZXN0MiwgbDJGQ19TQzAxXzB0bzggPiAxIHwgbDJGQ19TQzA3XzB0bzggPiAxIHwgbDJGQ19TQzEwXzB0bzggPiAxKQojIHdyaXRlLmNzdih4ID0gdGVzdDIsIGZpbGUgPSAiYWxsX2lvbkNoYW5uZWxfRXhwcmVzc2lvbi5jc3YiKQojIHdyaXRlLmNzdih4ID0gdGVzdDMsIGZpbGUgPSAiYWxsVXByZWdfaW9uQ2hhbm5lbF9FeHByZXNzaW9uLmNzdiIpCiMgd3JpdGUuY3N2KHggPSB0ZXN0NCwgZmlsZSA9ICJhdExlYXN0T25lVXByZWdfaW9uQ2hhbm5lbF9FeHByZXNzaW9uLmNzdiIpCnRlc3Q1IDwtIGxvZzIodGVzdDRbLCAxOjldKzEpCnBoZWF0bWFwKHRlc3Q1LCBjbHVzdGVyX2NvbHMgPSBGLCBjbHVzdGVyX3Jvd3MgPSBGKQp0ZXN0NiA8LSBsb2cyKCh0ZXN0M1ssMTo5XSkrMSkKcGhlYXRtYXAodGVzdDYsIGNsdXN0ZXJfcm93cyA9IEYsIGNsdXN0ZXJfY29scyA9IEYpCnRlc3Q3IDwtIHRlc3Q1W3Jvd1N1bXModGVzdDUpPjMwLF0KcGhlYXRtYXAodGVzdDcsIGNsdXN0ZXJfcm93cyA9IEYsIGNsdXN0ZXJfY29scyA9IEYpCmBgYAoKYGBge3J9CiMgbG9hZChmaWxlPSJ1bnRyZWF0ZWRJZGxpbmdfREVBLlJEYXRhIikKCk9yZ0RCIDwtIG9yZy5Icy5lZy5kYgp1cHJlZ19nZW5lcyA8LSBzdWJzZXQocmVzdWx0c18wdG84ZCwgcGFkajwwLjA1ICYgbG9nMkZvbGRDaGFuZ2U+MikKZG93bnJlZ19nZW5lcyA8LXN1YnNldChyZXN1bHRzXzB0bzhkLCBwYWRqPDAuMDUgJiBsb2cyRm9sZENoYW5nZTwoLTIpKQoKZ2VuZUxpc3RfdXAgPC0gYXMudmVjdG9yKHVwcmVnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlKQpuYW1lcyhnZW5lTGlzdF91cCkgPC0gcm93bmFtZXModXByZWdfZ2VuZXMpCmdlbmVMaXN0X2Rvd24gPC0gYXMudmVjdG9yKGRvd25yZWdfZ2VuZXMkbG9nMkZvbGRDaGFuZ2UpCm5hbWVzKGdlbmVMaXN0X2Rvd24pIDwtIHJvd25hbWVzKGRvd25yZWdfZ2VuZXMpCgpnZW5lc191cCA8LSBhcy52ZWN0b3Iocm93bmFtZXModXByZWdfZ2VuZXMpKQpnZW5lc19kb3duIDwtIGFzLnZlY3Rvcihyb3duYW1lcyhkb3ducmVnX2dlbmVzKSkKIyBuYW1lcyhnZW5lTGlzdCkgPC0gcm93bmFtZXMocmVzdWx0c18wdG84ZCkKZ2VuZXNfdXBfRU5UUkVaSUQgPC0gYml0cihnZW5lc191cCwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBPcmdEQikkRU5UUkVaSUQKZ2VuZXNfZG93bl9FTlRSRVpJRCA8LSBiaXRyKGdlbmVzX2Rvd24sIGZyb21UeXBlID0gIlNZTUJPTCIsIHRvVHlwZSA9ICJFTlRSRVpJRCIsIE9yZ0RiID0gT3JnREIpJEVOVFJFWklECgojIEdyb3VwIEdPCmdnb191cCA8LSBjbHVzdGVyUHJvZmlsZXI6Omdyb3VwR08oZ2VuZSAgICAgPSBnZW5lc191cF9FTlRSRVpJRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICA9IE9yZ0RCLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCAgICA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKQpnZ29fdXBfZGYgPC0gYXMuZGF0YS5mcmFtZShnZ29fdXApCmdnb191cF9kZiA8LSBnZ29fdXBfZGZbb3JkZXIoLWdnb191cF9kZiRDb3VudCksXSAKCmdnb19kb3duIDwtIGNsdXN0ZXJQcm9maWxlcjo6Z3JvdXBHTyhnZW5lID0gZ2VuZXNfZG93bl9FTlRSRVpJRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICA9IE9yZ0RCLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCAgICA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKQojIFZpZXcoYXMuZGF0YS5mcmFtZShnZ29fZG93bikpCgojIEdPIG92ZXItcmVwcmVzZW50YXRpb24gdGVzdAplZ29fZ2VuZXNVcCA8LSBjbHVzdGVyUHJvZmlsZXI6OmVucmljaEdPKGdlbmUgID0gZ2VuZXNfdXBfRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiICAgICAgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ICAgICAgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZSAgICAgID0gVFJVRSkKCiMgVmlldyhhcy5kYXRhLmZyYW1lKGVnb19nZW5lc1VwKSkKCmVnb19nZW5lc0Rvd24gPC0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lICA9IGdlbmVzX2Rvd25fRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiICAgICAgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ICAgICAgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZSAgICAgID0gVFJVRSkKCiMgVmlldyhhcy5kYXRhLmZyYW1lKGVnb19nZW5lc0Rvd24pKQoKIyBra19nZW5lc1VwIDwtIGVucmljaEtFR0coZ2VuZSA9IGdlbmVzX3VwX0VOVFJFWklELAojICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICdoc2EnLAojICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiMgVmlldyhhcy5kYXRhLmZyYW1lKGtrX2dlbmVzVXApKQojIAojIGtrX2dlbmVzRG93biA8LSBlbnJpY2hLRUdHKGdlbmUgPSBnZW5lc19kb3duX0VOVFJFWklELAojICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICdoc2EnLAojICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiMgVmlldyhhcy5kYXRhLmZyYW1lKGtrX2dlbmVzRG93bikpCgojIGVnb19HU0VBX3VwIDwtIGdzZUdPKGdlbmVMaXN0ID0gZ2VuZUxpc3RfdXAsCiMgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgPSBPcmdEQiwKIyAgICAgICAgICAgICAgIG9udCAgICAgICAgICA9ICJCUCIsCiMgICAgICAgICAgICAgICBuUGVybSAgICAgICAgPSAxMDAwLAojICAgICAgICAgICAgICAgbWluR1NTaXplICAgID0gMTAwLAojICAgICAgICAgICAgICAgbWF4R1NTaXplICAgID0gNTAwLAojICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSwKIyAgICAgICAgICAgICAgIHZlcmJvc2UgICAgICA9IEZBTFNFKQoKIyBiYXJwbG90KGdnb191cCwgb3JkZXI9VCkKIyBiYXJwbG90KGdnb19kb3duKQoKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQplbnJpY2hwbG90Ojpkb3RwbG90KGVnb19nZW5lc1VwKSArIGdndGl0bGUoIkdPIE92ZXItcmVwcmVzZW50YXRpb24gVXByZWd1bGF0ZWQgR2VuZXMiKSArCiAgbGFicyh4PSJHZW5lIFJhdGlvIiwgeT0iR08gVGVybXMiKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQoKZW5yaWNocGxvdDo6ZG90cGxvdChlZ29fZ2VuZXNEb3duKSArIGdndGl0bGUoIkdPIE92ZXItcmVwcmVzZW50YXRpb24gRG93bnJlZ3VsYXRlZCBHZW5lcyIpICsKICBsYWJzKHg9IkdlbmUgUmF0aW8iLCB5PSJHTyBUZXJtcyIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIikpCgojIGVtYXBwbG90KGVnb19nZW5lc1VwKQojIGVtYXBwbG90KGVnb19nZW5lc0Rvd24pCmVucmljaHBsb3Q6OmNuZXRwbG90KGVnb19nZW5lc1VwLCBjYXRlZ29yeVNpemU9InB2YWx1ZSIsIGNvbG9yLnBhcmFtcyA9IGxpc3QoZm9sZENoYW5nZSA9IGdlbmVMaXN0X3VwKSkKZW5yaWNocGxvdDo6Y25ldHBsb3QoZWdvX2dlbmVzRG93biwgY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBjb2xvci5wYXJhbXMgPSBsaXN0KGZvbGRDaGFuZ2UgPSBnZW5lTGlzdF9kb3duKSkKCgplZ29fZ2VuZXNVcF9kZiA8LSBhcy5kYXRhLmZyYW1lKGVnb19nZW5lc1VwKSAKZWdvVXAgPC0gZWdvX2dlbmVzVXBfZGZbb3JkZXIoLWVnb19nZW5lc1VwX2RmJENvdW50KSxdCiMgc29ydGVkX2Vnb1VwX3RvcDEwIDwtIGhlYWQoZWdvVXAsIDEwKQplZ29VcF9nZW5lcyA8LSBzdHJzcGxpdChlZ29VcCRnZW5lSUQsICIvIiwgZml4ZWQ9VFJVRSkKIyBlZ29VcF90b3AxMF9nZW5lc19hbGwgPC0gdW5saXN0KHN0cnNwbGl0KGhlYWQoZWdvVXAsIDEwKSRnZW5lSUQsICJbL10iKSkKIyBlZ29VcF90b3AxMF9nZW5lc19ncm91cCA8LSBzdHJzcGxpdChzb3J0ZWRfZWdvVXBfdG9wMTAkZ2VuZUlELCAiWy9dIikKIyBlZ29VcF90b3AxMF9nZW5lc191bmlxdWUgPC0gdW5pcXVlKGVnb1VwX3RvcDEwX2dlbmVzKQojIHRhYmxlKGVnb1VwX3RvcDEwX2dlbmVzKQojIGVnb1VwX2dlbmVzQnlHcm91cCA8LSBhcy5kYXRhLmZyYW1lKHQocGx5cjo6bGRwbHkoZWdvVXBfdG9wMTBfZ2VuZXNfZ3JvdXAsIHJiaW5kKSkpCiMgY29sbmFtZXMoZWdvVXBfZ2VuZXNCeUdyb3VwKSA8LSBzb3J0ZWRfZWdvVXBfdG9wMTAkRGVzY3JpcHRpb24KIyBlZ29VcF9nZW5lc0J5R3JvdXBfaW9uT25seSA8LSBlZ29VcF9nZW5lc0J5R3JvdXBbLGMoMTo2LDg6MTApXQoKIyB3cml0ZS5jc3YoZWdvVXBfZ2VuZXNCeUdyb3VwLCBmaWxlPSJ0b3AxMEdPdGVybXNVcHJlZ3VsYXRlZF9nZW5lTWVtYmVyc2hpcC5jc3YiKQojIGlvbkdlbmVzIDwtIHVuaXF1ZSh1bmxpc3QoZWdvVXBfZ2VuZXNCeUdyb3VwX2lvbk9ubHkpKQojIAojIGVuc2VtYmwgPSB1c2VFbnNlbWJsKGJpb21hcnQ9ImVuc2VtYmwiLCBkYXRhc2V0PSJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiKQojIElEcyA8LSBhcy5jaGFyYWN0ZXIoaW9uR2VuZXMpCiMgZ2VuZVVwSUQgPC0gbmFtZXMoZ2VuZUxpc3RfdXApCiMgZ2VuZURvd25JRCA8LSBuYW1lcyhnZW5lTGlzdF9kb3duKQojIGdlbmVkZXNjX2lvbiA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ2V4dGVybmFsX2dlbmVfbmFtZScsJ2Rlc2NyaXB0aW9uJyksIGZpbHRlcnMgPSAnZXh0ZXJuYWxfZ2VuZV9uYW1lJywgdmFsdWVzID0gSURzLCBtYXJ0ID1lbnNlbWJsKQojIHdyaXRlLmNzdihnZW5lZGVzY19pb24sIGZpbGUgPSAiaW9uQ2hhbm5lbEdlbmVzX2Rlc2NyaXB0aW9uLmNzdiIpCgojIGdlbmVkZXNjX1VwIDwtIGdldEJNKGF0dHJpYnV0ZXM9YygnZXh0ZXJuYWxfZ2VuZV9uYW1lJywnZGVzY3JpcHRpb24nKSwgZmlsdGVycyA9ICdleHRlcm5hbF9nZW5lX25hbWUnLCB2YWx1ZXMgPSBnZW5lVXBJRCwgbWFydCA9ZW5zZW1ibCkKIyB3cml0ZS5jc3YoZ2VuZWRlc2NfVXAsIGZpbGUgPSAidXByZWd1bGF0ZWRHZW5lc19kZXNjcmlwdGlvbi5jc3YiKQojIGdlbmVkZXNjX0Rvd24gPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCdleHRlcm5hbF9nZW5lX25hbWUnLCdkZXNjcmlwdGlvbicpLCBmaWx0ZXJzID0gJ2V4dGVybmFsX2dlbmVfbmFtZScsIHZhbHVlcyA9IGdlbmVEb3duSUQsIG1hcnQgPWVuc2VtYmwpCiMgd3JpdGUuY3N2KGdlbmVkZXNjX0Rvd24sIGZpbGUgPSAiZG93bnJndWxhdGVkR2VuZXNfZGVzY3JpcHRpb24uY3N2IikKYGBgCgoKYGBge3J9CmdlbmVMaXN0X2FsbCA8LSBhcy52ZWN0b3IocmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfYWxsKSA8LSByb3duYW1lcyhyZXN1bHRzXzB0bzhkKQphIDwtIG5hbWVzKGdlbmVMaXN0X2FsbCkKZ2VuZXNfRU5UUkVaSUQgPC0gYml0cihhLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IE9yZ0RCKSRFTlRSRVpJRApuYW1lcyhnZW5lTGlzdF9hbGwpIDwtIGdlbmVzX0VOVFJFWklECgpnZW5lX2RmIDwtIGRhdGEuZnJhbWUoRW50cmV6PW5hbWVzKGdlbmVMaXN0X2FsbCksIEhHTkM9YSwgRkM9Z2VuZUxpc3RfYWxsKQpnZW5lX2RmIDwtIGdlbmVfZGZbYWJzKGdlbmVfZGYkRkMpID4gMSxdCmdlbmVfZGYkZ3JvdXAgPC0gInVwcmVndWxhdGVkIgpnZW5lX2RmJGdyb3VwW2dlbmVfZGYkRkMgPCAwXSA8LSAiZG93bnJlZ3VsYXRlZCIKZ2VuZV9kZiRvdGhlcmdyb3VwIDwtICJBIgpnZW5lX2RmJG90aGVyZ3JvdXBbYWJzKGdlbmVfZGYkRkMpID4gMl0gPC0gIkIiCgpmb3JtdWxhX3JlcyA8LSBjb21wYXJlQ2x1c3RlcihFbnRyZXp+Z3JvdXArb3RoZXJncm91cCwgZGF0YT1nZW5lX2RmLCBmdW49ImVucmljaEtFR0ciKQpoZWFkKGFzLmRhdGEuZnJhbWUoZm9ybXVsYV9yZXMpKQoKYGBgCiMjIyMgMy4gRXhwbG9yaW5nIFJlc3VsdHMKCmBgYHtyfQpwbG90TUEocmVzLCB5bGltPWMoLTIsMikpCnBsb3RDb3VudHMoZGRzLCBnZW5lPXdoaWNoLm1pbihyZXMkcGFkaiksIGludGdyb3VwPSJ0cmVhdG1lbnQiKQoKYGBgCgojIyMjIExvZyBub3JtYWxpemUgcmVzdWx0cyAjIyMjCmBgYHtyfQoKIyBub3JtYWxpemVkQ291bnRzIDwtIHQoIHQoY291bnRzKGRkcykpIC8gc2l6ZUZhY3RvcnMoZGRzKSApCgojbG9nMiBub3JtYWxpemVkIGNvdW50cwpybGQyIDwtIHJsb2coZGRzLCBibGluZCA9IEZBTFNFKQojIHNhdmUocmxkMiwgZmlsZSA9ICJSTEQyX1NDMSw3LDEwX1RpbWVjb3Vyc2VfaG1hcC5SRGF0YSIpCgojIGxvYWQoIlJMRDJfU0MxLDcsMTBfVGltZWNvdXJzZV9obWFwLlJEYXRhIikKYGBgCgojIyMjIENsdXN0ZXJpbmcgIyMjCgpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHJsZDIpKSkKc2FtcGxlRGlzdHMKCnNhbXBsZURpc3RNYXRyaXggPC0gYXMubWF0cml4KCBzYW1wbGVEaXN0cyApCnJvd25hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIHBhc3RlKHJsZDIkdHJlYXRtZW50LCBybGQyJGNlbGwsIHNlcCA9ICIgLSAiICkKY29sbmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gTlVMTApjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBzYW1wbGVEaXN0cywKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gc2FtcGxlRGlzdHMsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCgpwb2lzZCA8LSBQb2lDbGFDbHU6OlBvaXNzb25EaXN0YW5jZSh0KGNvdW50cyhkZHMpKSkKc2FtcGxlUG9pc0Rpc3RNYXRyaXggPC0gYXMubWF0cml4KCBwb2lzZCRkZCApCnJvd25hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBwYXN0ZSggZGRzJGRleCwgZGRzJGNlbGwsIHNlcD0iIC0gIiApCmNvbG5hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBOVUxMCnBoZWF0bWFwKHNhbXBsZVBvaXNEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBwb2lzZCRkZCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gcG9pc2QkZGQsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCm1kcyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMikpICAlPiUKICAgICAgICAgY2JpbmQoY21kc2NhbGUoc2FtcGxlRGlzdE1hdHJpeCkpCmdncGxvdChtZHMsIGFlcyh4ID0gYDFgLCB5ID0gYDJgLCBjb2xvciA9IGNlbGwsIHNoYXBlID0gdHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsgY29vcmRfZml4ZWQoKSArIHRoZW1lX2J3KCkgKwogIHhsYWIoIlBDMSIpICsgeWxhYigiUEMyIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwKICAgICAgICAjIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKIyBsaWJyYXJ5KCJnZW5lZmlsdGVyIikKdG9wVmFyR2VuZXMgPC0gaGVhZChvcmRlcihyb3dWYXJzKGFzc2F5KHJsZDIpKSwgZGVjcmVhc2luZyA9IFRSVUUpLCA1MDAwKQptYXQgIDwtIGFzc2F5KHJsZDIpWyB0b3BWYXJHZW5lcywgXQptYXQgIDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKYW5ubyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMilbLCBjKCJjZWxsIiwidHJlYXRtZW50IildKQpuYW1lcyhhbm5vKSA8LSBjKCJDZWxsIiwgIlRyZWF0bWVudCIpCmFubm90YXRpb25fY29sb3JzID0gbGlzdCgKICBDZWxsID0gYyhTQzAxPSJyZWQyIiwgU0MwNz0iZ3JlZW4yIiwgU0MxMD0iYmx1ZTIiKSwKICBUcmVhdG1lbnQgPSBjKCIwIj0iY3lhbjIiLCAiMyI9ImRhcmtvcmFuZ2UiLCAiOCI9ImRhcmtvcmNoaWQiKSkKcGhlYXRtYXAobWF0LCBhbm5vdGF0aW9uX2NvbCA9IGFubm8sIHNob3dfcm93bmFtZXMgPSBGLCBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uX2NvbG9ycykKYGBgCgojIyMjIFRpbWUgc2VyaWVzIGFuYWx5c2lzICMjIyMKCiMgMSBERVNlcTIgdGltZSBzZXJpZXMgYW5hbHlzaXMKYGBge3J9CiMgYnJvd3NlVmlnbmV0dGVzKCJybmFzZXFHZW5lIikKCmRkc1RDIDwtIERFU2VxKGRkcywgdGVzdD0iTFJUIiwgcmVkdWNlZCA9IH4gY2VsbCArIHRyZWF0bWVudCkKcmVzVEMgPC0gcmVzdWx0cyhkZHNUQykKcmVzVEMkc3ltYm9sIDwtIG1jb2xzKGRkc1RDKSRzeW1ib2wKIyBoZWFkKHJlc1RDW29yZGVyKHJlc1RDJHBhZGopLF0sIDQpCgp0YyA8LSBwbG90Q291bnRzKGRkc1RDLCB3aGljaC5taW4ocmVzVEMkcGFkaiksIAogICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0cmVhdG1lbnQiLCJjZWxsIiksIHJldHVybkRhdGEgPSBUUlVFKQoKZGRzVENbd2hpY2gubWluKHJlc1RDJHBhZGopLF0KCmdncGxvdCh0YywKICBhZXMoeCA9IHJlcChjKDAsMyw4KSwgZWFjaD05KSwgeSA9IGNvdW50LCBjb2xvciA9IGNlbGwsIGdyb3VwID0gY2VsbCkpICsgCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbWV0aG9kID0gImxvZXNzIikgKyBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lX2J3KCkgKwogIGdndGl0bGUoIlRpbWUgQ291cnNlIEV4cHJlc3Npb24gb2YgUERLNCIpICsKICBsYWJzKHg9IlRpbWUgKGRheXMpIiwgeT0iR2VuZSBDb3VudCIpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKcmVzdWx0c05hbWVzKGRkc1RDKQoKYmV0YXMgPC0gY29lZihkZHNUQykKY29sbmFtZXMoYmV0YXMpCgp0b3BHZW5lcyA8LSBoZWFkKG9yZGVyKHJlc1RDJHBhZGopLDUwKQptYXQgPC0gYmV0YXNbdG9wR2VuZXMsIC1jKDEsMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKcGhlYXRtYXAobWF0LCBicmVha3M9c2VxKGZyb209LXRociwgdG89dGhyLCBsZW5ndGg9MTAxKSwKICAgICAgICAgY2x1c3Rlcl9jb2w9RkFMU0UpCmBgYAoKCiMjIyBOT1RFCk9yaWdpbmFsIGNvZGUgYmVsb3cgcHJvZHVjZWQgbWFueSBtZXNzYWdlcyBvZiBgTm8gaWQgdmFyaWFibGVzOyB1c2luZyBhbGwgYXMgbWVhc3VyZSB2YXJpYWJsZXNgOyBwcmVzdW1hYmx5IGEgbGluZSBmb3IgZWFjaCBnZW5lLiBUaGlzIGlzIGR1ZSB0byB0aGUgYG1lbHRgIGZ1bmN0aW9uIG5vdCBoYXZpbmcgYW55IGlkIHZhcmlhYmxlcyB0byB1c2UuICAKCgpSZWppZ2dlcmluZyBjb2RlIG5vdCB5ZXQgZmluaXNoZWQuICBTaG91bGQgcHJvYmFibHkgdXNlIApgYGB7ciBTdWJsaW5lIEFOT1ZBLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFfQojIDEuMSBBTk9WQSAtIGNvbXBhcmUgYnR3biBzdWJsaW5lcyAKIyBncm91cCA8LSBhcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX2Jhc2VsaW5lIDwtIGxpc3QoKQpUdWtleVNDMDd0b1NDMDEgPC0gbGlzdCgpClR1a2V5U0MxMHRvU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzEwdG9TQzA3IDwtIGxpc3QoKQpub3JtX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShhc3NheShybGQyKSlbYygnU0MwMV9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMycpXQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIE5ldyBjb2RlIGJ5IERSVCAjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHNhbXBfbmFtZXMgPC0gY29sbmFtZXMobm9ybV9kYXRhKQoKIyBjb21wYXJlU3ViY2xvbmVzIDwtIGZ1bmN0aW9uKGdlbmVfbmFtZSwgZGF0PW5vcm1fZGF0YSwgc2FtcF9uYW1lcz1OVUxMLCBncm91cD1OVUxMKQojIHsKIyAgICAgaWYoaXMubnVsbChncm91cCkpIGdyb3VwIDwtIGFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyAgICAgaWYoaXMubnVsbChzYW1wX25hbWVzKSkgc2FtcF9uYW1lcyA8LSBjb2xuYW1lcyhkYXQpCiMgICAgICMgZGZhID0gZGF0YSBmb3IgYW5hbHlzaXMKIyAgICAgZGZhIDwtIGRhdGEuZnJhbWUodmFsdWU9YXMubnVtZXJpYyh0KGRhdFtnZW5lX25hbWUsXSkpLCBncm91cD1ncm91cCkKIyAgICAgcm93bmFtZXMoZGZhKSA8LSBzYW1wX25hbWVzCiMgICAgIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGRmYSkKIyAgICAgYW5vdmFfYmFzZWxpbmUgPC0gc3VtbWFyeShmaXQpW1sxXV1bWydQcig+RiknXV1bMV0KIyAgICAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQojICAgICBwdmFsIDwtIGRhdGEuZnJhbWUocF9hZGo9cmVzdWx0cyRncm91cFssJ3AgYWRqJ10pCiMgICAgIHJvd25hbWVzKHB2YWwpIDwtIGMoIlR1a2V5U0MwN3RvU0MwMSIsIlR1a2V5U0MxMHRvU0MwMSIsIlR1a2V5U0MxMHRvU0MwNyIpCiMgICAgIG91dCA8LSBsaXN0KGFub3ZhX2Jhc2VsaW5lID0gYW5vdmFfYmFzZWxpbmUsCiMgICAgICAgICAgICAgICAgIHB2YWwgPSBwdmFsKQojICAgICAjIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQojICAgICAjIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQojICAgICAjIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXSAgICAKIyAgICAgcmV0dXJuKG91dCkKIyB9CgojIHRlbXAgPC0gbGFwcGx5KHJvd25hbWVzKG5vcm1fZGF0YSksIGNvbXBhcmVTdWJjbG9uZXMpCiMgYW5vdmFfcHZhbCA8LSBzYXBwbHkodGVtcCwgIltbIiwgMSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMgRW5kIG5ldyBjb2RlICMjIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpmb3IgKGdlbmUgaW4gMTpucm93KG5vcm1fZGF0YSkpIHsKICBnZW5lX25vcm1fZGF0YSA8LSBub3JtX2RhdGFbZ2VuZSxdCiAgIyBkMyA8LSBkYXRhLmZyYW1lKHkgPSBnZW5lX25vcm1fZGF0YSwgZ3JvdXAgPSBncm91cCkKICAjIGZpdCA8LSBsbSh5fmdyb3VwLCBkMykKICBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIGRhdGEuZnJhbWUodmFyaWFibGU9Y29sbmFtZXMoZ2VuZV9ub3JtX2RhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZT1hcy5udW1lcmljKHQoZ2VuZV9ub3JtX2RhdGEpKSkKICBnZW5lX25vcm1fZGF0YV9tZWx0JGdyb3VwIDwtIGdyb3VwCiAgZml0IDwtIGFvdih2YWx1ZX5ncm91cCwgZ2VuZV9ub3JtX2RhdGFfbWVsdCkKICAjIGFub3ZhX2xpc3RbZ2VuZV0gPC0gYW5vdmEoZml0KSQnUHIoPkYpJ1sxXQogIGFub3ZhX2Jhc2VsaW5lW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKCiAgCmFub3ZhX3B2YWwgPC0gdW5saXN0KGFub3ZhX2Jhc2VsaW5lKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzA3dG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MwN3RvU0MwMSkKVHVrZXlTQzEwdG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwMSkKVHVrZXlTQzEwdG9TQzA3X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwNykKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBhbm92YV9wdmFsKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBUdWtleVNDMDd0b1NDMDFfcHZhbCkKbm9ybV9kYXRhX3N0YXRzIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0cywgVHVrZXlTQzEwdG9TQzAxX3B2YWwpCm5vcm1fZGF0YV9zdGF0cyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHMsIFR1a2V5U0MxMHRvU0MwN19wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0cywgZmlsZSA9ICJzdWJjbG9uZUNvdW50c19hbm92YV90dWtleV9ERVNlcTIuUkRhdGEiKQoKIyBJZGVudGlmeSBnZW5lcyB0aGF0IGRpZmZlciBiZXR3ZWVuIGNsb25lcyBiYXNlZCBvbiAKIyBBTk9WQSBwLXZhbHVlIHdpdGggZGVmaW5lZCB0aHJlc2hvbGQKc2lnVGhyZXNoIDwtIDAuMDUKdGFibGUoYW5vdmFfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3dG9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMHRvU0MwMV9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTB0b1NDMDdfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c1siYW5vdmFfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGwgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzWyJhbm92YV9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNbIlR1a2V5U0MwN3RvU0MwMV9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c1siVHVrZXlTQzEwdG9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzWyJUdWtleVNDMTB0b1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c1tzaWdJbmRlY2llcyxdKQpzaWdEaWZmR2VuZXNBbGwgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzW3NpZ0luZGVjaWVzQWxsLF0pCmBgYAoKIyAyLiBBTk9WQSBidHduIHRpbWUgcG9pbnRzICYgc2hhcmVkIGJ0d24gc3VibGluZXMpCmBgYHtyIFNDMDEgdGltZSBBTk9WQSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzAxX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMDFfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MwMV90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMDF0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMDFfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MwMXRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMDF0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgIyBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIG1lbHQoZ2VuZV9ub3JtX2RhdGEpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCgogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwMVtnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDFfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDFfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDFfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDFfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwMSkgIyBtYWtlIGFycmF5ClR1a2V5U0MwMV90aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTApClR1a2V5U0MwMV90aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTMpClR1a2V5U0MwMV90aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDF0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgYW5vdmFfU0MwMV9wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgVHVrZXlTQzAxX3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBUdWtleVNDMDFfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDEgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDEsIFR1a2V5U0MwMV90aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzAxdGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwMV90aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDFfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzAxX3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzAxIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzAxWyJhbm92YV9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDEgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDFbImFub3ZhX1NDMDFfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDFbIlR1a2V5U0MwMV90aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzAxWyJUdWtleVNDMDFfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwMVsiVHVrZXlTQzAxX3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzAxIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzAxW3NpZ0luZGVjaWVzX1NDMDEsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDEgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDFbc2lnSW5kZWNpZXNBbGxfU0MwMSxdKQpgYGAKCgpgYGB7ciBTQzA3IHRpbWUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9Cmdyb3VwPC1hcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX1NDMDcgPC0gbGlzdCgpClR1a2V5U0MwN190aW1lMCA8LSBsaXN0KCkKVHVrZXlTQzA3X3RpbWUzIDwtIGxpc3QoKQpUdWtleVNDMDdfdGltZTggPC0gbGlzdCgpCm5vcm1fZGF0YV9TQzA3dGltZSA8LSBhcy5kYXRhLmZyYW1lKGFzc2F5KHJsZDIpKVtjKCdTQzA3X2RheTBfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTNfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkzX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5M19yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheThfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXk4X3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5OF9yZXAzJyldCmZvciAoZ2VuZSBpbiAxOm5yb3cobm9ybV9kYXRhX1NDMDd0aW1lKSkgewogIGdlbmVfbm9ybV9kYXRhIDwtIG5vcm1fZGF0YV9TQzA3dGltZVtnZW5lLF0KICAjIGQzIDwtIGRhdGEuZnJhbWUoeSA9IGdlbmVfbm9ybV9kYXRhLCBncm91cCA9IGdyb3VwKQogICMgZml0IDwtIGxtKHl+Z3JvdXAsIGQzKQogICMgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBtZWx0KGdlbmVfbm9ybV9kYXRhKQogIGdlbmVfbm9ybV9kYXRhX21lbHQgPC0gZGF0YS5mcmFtZSh2YXJpYWJsZT1jb2xuYW1lcyhnZW5lX25vcm1fZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlPWFzLm51bWVyaWModChnZW5lX25vcm1fZGF0YSkpKQogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwN1tnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDdfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDdfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDdfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDdfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwNykgIyBtYWtlIGFycmF5ClR1a2V5U0MwN190aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTApClR1a2V5U0MwN190aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTMpClR1a2V5U0MwN190aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDd0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgYW5vdmFfU0MwN19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgVHVrZXlTQzA3X3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBUdWtleVNDMDdfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDcgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDcsIFR1a2V5U0MwN190aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzA3dGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzA3X3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwN190aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDdfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3X3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzA3IDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzA3WyJhbm92YV9TQzA3X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDcgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDdbImFub3ZhX1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDdbIlR1a2V5U0MwN190aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzA3WyJUdWtleVNDMDdfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwN1siVHVrZXlTQzA3X3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzA3IDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzA3W3NpZ0luZGVjaWVzX1NDMDcsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDcgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDdbc2lnSW5kZWNpZXNBbGxfU0MwNyxdKQpgYGAKCmBgYHtyIFNDMTAgdGltZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MxMCA8LSBsaXN0KCkKVHVrZXlTQzEwX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMTBfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MxMF90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMTB0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMTBfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MxMHRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMTB0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCRncm91cCA8LSBncm91cAogIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGdlbmVfbm9ybV9kYXRhX21lbHQpCiAgIyBhbm92YV9saXN0W2dlbmVdIDwtIGFub3ZhKGZpdCkkJ1ByKD5GKSdbMV0KICBhbm92YV9TQzEwW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MxMF90aW1lMFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMF90aW1lM1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMF90aW1lOFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQogIAp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKYW5vdmFfU0MxMF9wdmFsIDwtIHVubGlzdChhbm92YV9TQzEwKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzEwX3RpbWUwX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMCkKVHVrZXlTQzEwX3RpbWUzX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMykKVHVrZXlTQzEwX3RpbWU4X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lOCkKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gYXMuZGF0YS5mcmFtZShub3JtX2RhdGFfU0MxMHRpbWUpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBhbm92YV9TQzEwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBUdWtleVNDMTBfdGltZTBfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMTAsIFR1a2V5U0MxMF90aW1lM19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MxMCA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MxMCwgVHVrZXlTQzEwX3RpbWU4X3B2YWwpCgojIHNhdmUobm9ybV9kYXRhX3N0YXRzX1NDMTAsIGZpbGUgPSAic3ViY2xvbmVDb3VudHNfYW5vdmFfdHVrZXlfREVTZXEyX1NDMTB0aW1lLlJEYXRhIikKCiMgSWRlbnRpZnkgZ2VuZXMgdGhhdCBkaWZmZXIgYmV0d2VlbiBjbG9uZXMgYmFzZWQgb24gCiMgQU5PVkEgcC12YWx1ZSB3aXRoIGRlZmluZWQgdGhyZXNob2xkCnNpZ1RocmVzaCA8LSAwLjA1CnRhYmxlKGFub3ZhX1NDMTBfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzEwX3RpbWUwX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMF90aW1lM19wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTBfdGltZThfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzX1NDMTAgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMTBbImFub3ZhX1NDMTBfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGxfU0MxMCA8LSB3aGljaChub3JtX2RhdGFfc3RhdHNfU0MxMFsiYW5vdmFfU0MxMF9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MxMFsiVHVrZXlTQzEwX3RpbWUwX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMTBbIlR1a2V5U0MxMF90aW1lM19wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzEwWyJUdWtleVNDMTBfdGltZThfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzX1NDMTAgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMTBbc2lnSW5kZWNpZXNfU0MxMCxdKQpzaWdEaWZmR2VuZXNBbGxfU0MxMCA8LSByb3duYW1lcyhub3JtX2RhdGFfc3RhdHNfU0MxMFtzaWdJbmRlY2llc0FsbF9TQzEwLF0pCmBgYAoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmFsbF9TQ3NfdGltZSA8LSBSZWR1Y2UoaW50ZXJzZWN0LCAKICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHNpZ0RpZmZHZW5lc0FsbF9TQzAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnRGlmZkdlbmVzQWxsX1NDMDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdEaWZmR2VuZXNBbGxfU0MxMCkpCgpkZl9hbGxTQ3NfdGltZSA8LSBkYXRhLmZyYW1lKGdlbmUgPSBhbGxfU0NzX3RpbWUpCiMgZ2VuZXMgPC0gZGZfYWxsU0NzX3RpbWUkZ2VuZQojIEdfbGlzdCA8LSBnZXRCTShmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwgYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSx2YWx1ZXM9Z2VuZXMsbWFydD1tYXJ0KQptZXJnZShkZl9hbGxTQ3NfdGltZSxHX2xpc3QsYnkueD0iZ2VuZSIsYnkueT0iZW5zZW1ibF9nZW5lX2lkIikKCiMgd3JpdGUuY3N2KEdfbGlzdCwgZmlsZSA9ICJBTk9WQV9hbGxTQ3NUaW1lX3NoYXJlZF9nZW5lcy5jc3YiKQoKIyBDb21wYXJlIGdlbmUgbGlzdHMgZm9yIGJldHdlZW4gc3VibGluZXMgYW5kIHRpbWUKIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKZGJzIDwtIGxpc3RFbnJpY2hyRGJzKCkKZGJzIDwtIGMoIktFR0dfMjAxNiIsIAogICAgICAgICAiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTgiKQoKZW5yaWNoZWRfYWxsU0NzdGltZSA8LSBlbnJpY2hyKEdfbGlzdCRoZ25jX3N5bWJvbCwgZGJzKQoKS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUgPC0gZW5yaWNoZWRfYWxsU0NzdGltZVtbIktFR0dfMjAxNiJdXVsxOjUsXQpLRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtcyA8LSBmYWN0b3IoS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUkVGVybSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1LRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtKQoKZ2dwbG90KEtFR0dfdXByZWdfYWxsU0NzdGltZV90b3A1LCAKICAgICAgIGFlcyh4PVRlcm1zLCB5PS1sb2cxMChBZGp1c3RlZC5QLnZhbHVlKSkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeCA9ICJQYXRod2F5IFRlcm0iLCB5ID0gIi1sb2cxMChxLXZhbHVlKSIpICArCiAgdGhlbWVfYncoKSAgKyBnZ3RpdGxlKCJQYXRod2F5IEVucmljaG1lbnQgLSBLRUdHIDIwMTYiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSJib2xkIikpCiAgCgpgYGAKCiMgMyBKYWNrJ3MgbWV0aG9kIApgYGB7ciBldmFsPUZBTFNFfQojR3JhYiBhbGwgdGhlIG5hbWVzIGZyb20gcmVzIGluIHRoZSBERVNlcSBtYXRyaXgKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCgpjb3VudE1BVCA8LSBkYXRhLmZyYW1lKG5vcm1hbGl6ZWRDb3VudHNbdG9wR2VuZXMsXSkKCnN1YnJsID0gZGF0YS5mcmFtZShhc3NheShybGQyKSkKcmxNQVQgPSBkYXRhLmZyYW1lKHN1YnJsW3RvcEdlbmVzLF0pCgojTGFiZWxpbmcgcm93cyB3aXRoIEVOU0cgSURzCiMgY291bnRNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKGNvdW50TUFUKQojIGNvdW50TUFUJHBhZGogPSByZXNbdG9wR2VuZXMsInBhZGoiXQoKcmxNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKHJsTUFUKQpybE1BVCRwYWRqID0gcmVzW3RvcEdlbmVzLCJwYWRqIl0KCiMgbGlicmFyeShiaW9tYVJ0KQojIGVuc2VtYmwgPC0gdXNlTWFydCgiZW5zZW1ibCIpCiMgbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKIyBnZW5lcyA9IHJvdy5uYW1lcyhybE1BVCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKI0NoZWNrIGlmIGRhdGEgZml0cyBhIG5vcm1hbCBkaXN0cmlidXRpb24KIyBwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgoY291bnRNQVRbLDE6MjddKSkpKQpwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgocmxNQVRbLDE6MjddKSkpKQoKCiNybE1BVCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlcmVmb3JlIHdlIHdpbGwgdXNlIHRoaXMgaW4gdGhlIGhlYXRtYXAgY29uc3RydWN0aW9uCiNMYWJlbGluZyBkZiB3aXRoIGhnbmMgc3ltYm9scwpHRV9kYXRhIDwtIG1lcmdlKEdfbGlzdCwgcmxNQVQsIGJ5ID0gImVuc2VtYmxfZ2VuZV9pZCIpCgojTWFraW5nIHJvd25hbWVzIHVuaXF1ZSBoZ25jIHN5bWJvbHMKcm93bmFtZXMoR0VfZGF0YSkgPC0gbWFrZS5uYW1lcyhHRV9kYXRhWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI0F2ZXJhZ2luZyBybGQgYmV0d2VlbiB0cmlhbHMKQWNvbCA8LSBjKCJTQzAxX2RheTAiLAogICAgICAgICAgIlNDMDFfZGF5MyIsCiAgICAgICAgICAiU0MwMV9kYXk4IiwKICAgICAgICAgICJTQzA3X2RheTAiLAogICAgICAgICAgIlNDMDdfZGF5MyIsCiAgICAgICAgICAiU0MwN19kYXk4IiwKICAgICAgICAgICJTQzEwX2RheTAiLAogICAgICAgICAgIlNDMTBfZGF5MyIsCiAgICAgICAgICAiU0MxMF9kYXk4IikKZm9yKGkgaW4gMTpsZW5ndGgoQWNvbCkpewogIGogPSAyK2kKICBrID0gMiszKmkKICBHRV9kYXRhWyxBY29sW2ldXSA9IHJvd01lYW5zKEdFX2RhdGFbLGMoajprKV0pCn0KCgojQ2FsY3VsYXRpbmcgZm9sZCBjaGFuZ2VzIGFjcm9zcyBjb25kaXRpb25zIGluIGEgdHJpYW5ndWxhciBtYXRyaXggZm9ybQpHRV9tZWFuID0gR0VfZGF0YVssYygxLDIsMzA6MzkpXQpERVByb2MgPSBHRV9tZWFuCnN0YXJ0Y29sID0gNAplbmRjb2wgPSAxMgoKYWxsRkMgPC0gZnVuY3Rpb24oREVQcm9jLHN0YXJ0Y29sLGVuZGNvbCl7IAogIEdFX2ZvbGQgPSBERVByb2NbLC1jKHN0YXJ0Y29sOmVuZGNvbCldCiAgY29sdmVjID0gY29sbmFtZXMoREVQcm9jKVtzdGFydGNvbDplbmRjb2xdCiAgCiAgI0xhc3QgaW5kZXggaXMgYSBzZWxmIGNvbXBhcmlzb24gYW5kIGlzIHJlbW92ZWQKICBmb3IoayBpbiAxOihsZW5ndGgoY29sdmVjKS0xKSl7CiAgICAjU3RhcnQgd2l0aCBjb2x1bW4gdGhhdCBpcyAxIGF3YXkgZnJvbSBpbmRleCAKICAgIGZvcihqIGluIChrKzEpOmxlbmd0aChjb2x2ZWMpKXsKICAgICAgY29tcG5hbSA9IHBhc3RlMChjb2x2ZWNbal0sIi8iLGNvbHZlY1trXSkKICAgICAgI0xvb3AgdGhyb3VnaCBlYWNoIGdlbmUvcm93ICAKICAgICAgZm9yKGkgaW4gMTpucm93KERFUHJvYykpewogICAgICAgIGYgPSBERVByb2NbaSxjb2x2ZWNbal1dCiAgICAgICAgaCA9IERFUHJvY1tpLGNvbHZlY1trXV0KICAgICAgICAKICAgICAgICAjQ2FwdHVyZSB1cHJlZ3VsYXRpb24gYW5kIGRvd24gcmVndWxhdGlvbgogICAgICAgIGlmKGY+aCl7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAyXihmLWgpCiAgICAgICAgfWVsc2V7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAtMl4oaC1mKQogICAgICAgIH0KICAgICAgICAKICAgICAgfQogICAgfQogIH0KICAKICByZXR1cm4oR0VfZm9sZCkKICAKfQoKI1N1YnNldCBnZW5lLCB0aGVuIHBsb3QsIHRoZW4gc2F2ZSBwbG90CiNQZXJoYXBzIG1ha2UgaGVhdG1hcHMgd2l0aCBzY2FsZWQgeiBzY29yZXMKI0lzIHRoZXJlIGEgd2F5IHRvIGNvbnNvbGlkYXRlIHJlcGxpY2F0ZSB6IHNjb3Jlcz8gR2VvbWV0cmljIG1lYW4/IAojUmVndWxhciBtZWFuLCB0aGVuIHNjYWxlLgoKIyBJbXBSYXQgPSBjb2xuYW1lcyhHRV9mb2xkKVtjKDQsNSw2LDksMTIsMTQsMTcsMjEsMjQsMjUsMjYsMjcsMzAsMzIsMzYsMzcsMzgsMzkpXQoKI0xpc3Rpbmcgb2YgYWxsIGltcG9ydGFudCBjb21wYXJpc29ucz8KSW1wUmF0ID0gYygiU0MwMV9kYXkzL1NDMDFfZGF5MCIsICJTQzAxX2RheTgvU0MwMV9kYXkzIiwgIlNDMDFfZGF5OC9TQzAxX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkzL1NDMDdfZGF5MCIsICJTQzA3X2RheTgvU0MwN19kYXkzIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAKICAgICAgICAgICAiU0MxMF9kYXkzL1NDMTBfZGF5MCIsICJTQzEwX2RheTgvU0MxMF9kYXkzIiwgIlNDMTBfZGF5OC9TQzEwX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiLAogICAgICAgICAgICJTQzA3X2RheTMvU0MwMV9kYXkzIiwgIlNDMTBfZGF5My9TQzAxX2RheTMiLCAiU0MxMF9kYXkzL1NDMDdfZGF5MyIsCiAgICAgICAgICAgIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IiApCkltcF9mb2xkID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBJbXBSYXQpXQpJbXBfZm9sZDIgPSBJbXBfZm9sZFtyb3dTdW1zKGFicyhJbXBfZm9sZFssNDoyMV0pPj0xLjUpPj0xLF0KCiMgd3JpdGUudGFibGUoSW1wX2ZvbGQsIlNDMSw3LDEwLVRpbWVjb3Vyc2VQTFgtSW1wb3J0YW50RkNfMjAxODA3MjIudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GKQoKSW1wX2ZvbGQgPSByZWFkLmRlbGltKCJTQzEsNywxMC1UaW1lY291cnNlUExYLUltcG9ydGFudEZDXzIwMTgwNzIyLnR4dCIsIHNlcD0iXHQiKQoKI1N1YnNldCB0aGUgTEYgbWVhbiBvZiBpbXBvcnRhbnQgZ2VuZXMgZnJvbSBMb2cyIEZvbGQgQ2hhbmdlIChMRkMpIGNvbXBhcmlzb24gZGF0YSBmcmFtZS4KR0VfSW1wID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lSW1wX2ZvbGQyJGVuc2VtYmxfZ2VuZV9pZCkKCk5lY3JvID0gcmVhZC5kZWxpbSgiS0VHR05lY3JvcHRvc2lzX2hzYTA0MjE3XzA2LTI1LTE4LnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKTmVjcm8gPSBOZWNyb1tyb3dTdW1zKGlzLm5hKE5lY3JvKSkgPT0gMCwgXQpERV9OZWNybyA9IG1lcmdlKEdFX0ltcCwgTmVjcm8sIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX05lY3JvKSA9IG1ha2UubmFtZXMoREVfTmVjcm9bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChERV9OZWNyb1szOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFKQojIHdyaXRlLnRhYmxlKERFX05lY3JvLCAiS0VHR05lY3JvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCgpBcG9wID0gcmVhZC5kZWxpbSgiS0VHR0Fwb3B0b3Npc19oc2EwNDIxMF8wNi0yNS0xOC50eHQiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCkFwb3AgPSBBcG9wW3Jvd1N1bXMoaXMubmEoQXBvcCkpID09IDAsIF0KREVfQXBvcCA9IG1lcmdlKEdFX0ltcCksIEFwb3AsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0Fwb3ApID0gbWFrZS5uYW1lcyhERV9BcG9wWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfQXBvcFszOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFLCBzY2FsZSA9ICJyb3ciKQojIHdyaXRlLnRhYmxlKERFX0Fwb3AsICJLRUdHQXBvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCkZlcnIgPSByZWFkLmRlbGltKCJLRUdHRmVycm9wdG9zaXNfaHNhMDQyMTZfMDYtMjUtMTgudHh0IiwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpGZXJyID0gRmVycltyb3dTdW1zKGlzLm5hKEZlcnIpKSA9PSAwLCBdCkRFX0ZlcnIgPSBtZXJnZShHRV9JbXAsIEZlcnIsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0ZlcnIpID0gbWFrZS5uYW1lcyhERV9GZXJyWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfRmVycls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCiMgd3JpdGUudGFibGUoREVfRmVyciwgIktFR0dGZXJyb3B0b3NpcyBTQzEsNywxMCBERVNlcSBMUlQudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UpCgoKYGBgCgojIyMjIDQuIERpZmZlcmVudCBMQyBjb21wYXJpc29ucy4gQmV0d2VlbiBzdWJjbG9uZXMgYW5kIGF0IGJhc2VsaW5lIHZzIGlkbGluZy4KWnNjb3JlIGhlYXRtYXBzIG9mIHJlbGV2YW50IGNvbXBhcmlzb25zIGNhbiBiZSBtYWRlIGFzIGluIGFib3ZlIHRvIHZpc3VhbGl6ZS4KCmBgYHtyIGV2YWw9RkFMU0V9CgojVVNFUyBBQk9WRSBDT0RFIFRPIExJTkUgMjgwLiBSdW4gdGhhdCBwc2V1ZG8tZnVuY3Rpb24uCgojIGxpYnJhcnkocGhlYXRtYXApCiNDb21wYXJpc29ucyBvZiBkaWZFeCBiZXR3ZWVuIHN1YmNsb25lcyBhdCBiYXNlbGluZSBhbmQgaWRsaW5nCkJldHdlZW5CYXNlID0gYygiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiKQpCZXR3ZWVuSWRsZSA9IGMoIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IikKIAoKI1Vuc3VyZSBvZiBob3cgc3RyaWN0IHRvIG1ha2UgdGhlIGN1dG9mZi4gU2hvdWxkIGFsbCB0aGUgZ2VuZXMgYmV0d2VlbiBjbG9uZXMgYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkICgzKSBvciBpcyBhIHNpbmdsZSBkaWZmZXJlbmNlIHN1ZmZpY2llbnQ/CkJ0d19iID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBCZXR3ZWVuQmFzZSldCkJ0d19iMSA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MSxdCkJ0d19iMiA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MixdCkJ0d19iMyA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MyxdCgpCdHdfaSA9IEdFX2ZvbGRbLGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoZ25jX3N5bWJvbCIsICJwYWRqIiwgQmV0d2VlbklkbGUpXQpCdHdfaTEgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTEsXQpCdHdfaTIgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTIsXQpCdHdfaTMgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTMsXQoKI1RoaXMgZG9lcyBub3QgYWNjb3VudCBmb3Igc2FtZSBkaXJlY3Rpb24gb2YgY2hhbmdlLiBUaGlzIGNhbiBiZSBwbG90dGVkIGluIGEgaGVhdG1hcCB0byB2aWV3LgojTWVtYmVycyB0aGF0IHdlcmUgImxvc3QiIGJ5IHRoZSBiYXNlbGluZSBjb25kaXRpb24gYXQgYmVpbmcgZGlmZmVyZW50LiBXZXJlIG5vIGxvbmdlciBmb3VuZCBkaWZmRXggYmV0d2VlbiB0aGUgc3ViY2xvbmVzIHdoZW4gY29tcGFyaW5nIGJhc2VsaW5lIHRvIGlkbGluZyBERUdzLgpMb3N0REVHX2JfMSA9IHN1YnNldChCdHdfYjEsIUJ0d19iMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kxJGVuc2VtYmxfZ2VuZV9pZCkKTG9zdERFR19iXzIgPSBzdWJzZXQoQnR3X2IyLCFCdHdfYjIkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMiRlbnNlbWJsX2dlbmVfaWQpCkxvc3RERUdfYl8zID0gc3Vic2V0KEJ0d19iMywgIUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjTWFrZSBoZWF0bWFwCkxvc3RERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMyRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfM19tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8zX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKI01lbWJlcnMgdGhhdCByZW1haW5lZCBkaWZmZXJlbnQuIApTdGF0aWNERUdfYl8xID0gc3Vic2V0KEJ0d19iMSxCdHdfYjEkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQpClN0YXRpY0RFR19iXzIgPSBzdWJzZXQoQnR3X2IyLEJ0d19iMiRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCkKU3RhdGljREVHX2JfMyA9IHN1YnNldChCdHdfYjMsIEJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjU29tZSBIR05DX3N5bWJvbHMgYXJlIGR1cGxpY2F0ZXMhIEkgc3dpdGNoZWQgdG8gZW5zZW1ibF9nZW5lX2lkIHRvIGZpeC4KU3RhdGljREVHX2lfMyA9IHN1YnNldChCdHdfaTMsIEJ0d19pMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IzJGVuc2VtYmxfZ2VuZV9pZCkKCgojI01ha2UgaGVhdG1hcApTdGF0aWNERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVTdGF0aWNERUdfYl8zJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhbikgPSBtYWtlLm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKFN0YXRpY0RFR19iXzNfbWVhbls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgoKI01lbWJlcnMgdGhhdCAiZ2FpbmVkIiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBzdWJjbG9uZXMgaW4gaWRsaW5nLiAgCkdhaW5ERUdfaV8xID0gc3Vic2V0KEJ0d19pMSwgIUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IxJGVuc2VtYmxfZ2VuZV9pZCkKR2FpbkRFR19pXzIgPSBzdWJzZXQoQnR3X2kyLCAhQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCVpbiVCdHdfYjIkZW5zZW1ibF9nZW5lX2lkKQpHYWluREVHX2lfMyA9IHN1YnNldChCdHdfaTMsICFCdHdfaTMkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQpCgojI01ha2UgaGVhdG1hcApHYWluREVHX2lfM19tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lR2FpbkRFR19pXzMkZW5zZW1ibF9nZW5lX2lkKQpyb3cubmFtZXMoR2FpbkRFR19pXzNfbWVhbikgPSBtYWtlLm5hbWVzKEdhaW5ERUdfaV8zX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChHYWluREVHX2lfM19tZWFuWzQ6MTJdLGNsdXN0ZXJfY29scz1GQUxTRSwgc2NhbGUgPSAicm93IikKCgojTWVtYmVycyB0aGF0IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gaWRsaW5nICg4ZGF5KSBhbmQgYmFzZWxpbmUgd2l0aGluIHN1YmNsb25lcy4gVGhvc2Ugd2l0aCBzaGFyZWQgZGlmZkV4IG1heSBiZSBjb252ZXJnZW50IGFjcm9zcyBtdWx0aXBsZSBzdWJjbG9uZXMgZGVwZW5kaW5nIG9uIGRpcmVjdGlvbiBvZiBleHByZXNpc29uIGNoYW5nZS4KRW5kcG9pbnQgPSBjKCJTQzAxX2RheTgvU0MwMV9kYXkwIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAiU0MxMF9kYXk4L1NDMTBfZGF5MCIpCkJ0b0lkbGUgPSBHRV9mb2xkWyxjKCJlbnNlbWJsX2dlbmVfaWQiLCAiaGduY19zeW1ib2wiLCAicGFkaiIsIEVuZHBvaW50KV0KQnRvSWRsZV8xID0gQnRvSWRsZVtyb3dTdW1zKGFicyhCdG9JZGxlWyw0OjZdKT49MS41KT49MSxdCkJ0b0lkbGVfMiA9IEJ0b0lkbGVbcm93U3VtcyhhYnMoQnRvSWRsZVssNDo2XSk+PTEuNSk+PTIsXQpCdG9JZGxlXzMgPSBCdG9JZGxlW3Jvd1N1bXMoYWJzKEJ0b0lkbGVbLDQ6Nl0pPj0xLjUpPj0zLF0KCiMjTWFrZSBoZWF0bWFwCkJ0b0lkbGVfMl9tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lQnRvSWRsZV8yJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuKSA9IG1ha2UubmFtZXMoQnRvSWRsZV8yX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQoKQnRvSWRsZV8yX21lYW5faW5jRXhwID0gQnRvSWRsZV8yX21lYW5bd2hpY2goQnRvSWRsZV8yX21lYW4kU0MwMV9kYXkwIDwgQnRvSWRsZV8yX21lYW4kU0MwMV9kYXk4KSxdCkJ0b0lkbGVfMl9tZWFuX2luY0V4cCA9IEJ0b0lkbGVfMl9tZWFuX2luY0V4cFt3aGljaChCdG9JZGxlXzJfbWVhbl9pbmNFeHAkU0MwN19kYXkwIDwgQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMDdfZGF5OCksXQpCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2goQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMTBfZGF5MCA8IEJ0b0lkbGVfMl9tZWFuX2luY0V4cCRTQzEwX2RheTgpLF0KCkxvc3RERUdfYl8yX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMiRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfMl9tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzJfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8yX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKQnRvSWRsZUluY0V4cF9ERWJldHdlZW5TQ3MgPSBCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2gocm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuX2luY0V4cCkgJWluJSByb3cubmFtZXMoTG9zdERFR19iXzJfbWVhbikpLF0KCnBoZWF0bWFwKEJ0b0lkbGVfMl9tZWFuX2luY0V4cFs0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgojIGxpYnJhcnkoZGV2dG9vbHMpCiMgIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKIyBsaWJyYXJ5KGVucmljaFIpCmRicyA8LSBsaXN0RW5yaWNockRicygpCmhlYWQoZGJzKQpkYnMgPC0gYygiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiLCAiR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiLCAiR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiKQoKZW5yaWNoZWQgPC0gZW5yaWNocihyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSwgZGJzKQoKVmlldyhlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dKQpWaWV3KGVucmljaGVkW1siR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiXV0pClZpZXcoZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSkKCmVucmljaGVkX01GX3NpZyA8LSBlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dW2VucmljaGVkW1siR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiXV0kQWRqdXN0ZWQuUC52YWx1ZTwwLjA1LF0KZW5yaWNoZWRfTUZfc2lnX2RmIDwtIGRhdGEuZnJhbWUoZW5yaWNoZWRfTUZfc2lnWyxjKDEsNCw5KV0pCndyaXRlLmNzdihlbnJpY2hlZF9NRl9zaWdfZGYsICJlbnJpY2hlZF9NRl9zaWduaWZpY2FudC5jc3YiKQoKZW5yaWNoZWRfQlBfc2lnIDwtIGVucmljaGVkW1siR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiXV1bZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSRBZGp1c3RlZC5QLnZhbHVlPDAuMDUsXQplbnJpY2hlZF9CUF9zaWdfZGYgPC0gZGF0YS5mcmFtZShlbnJpY2hlZF9CUF9zaWdbLGMoMSw0LDkpXSkKIyB3cml0ZS5jc3YoZW5yaWNoZWRfQlBfc2lnX2RmLCAiZW5yaWNoZWRfQlBfc2lnbmlmaWNhbnQuY3N2IikKCkdpbmlfc2NHZW5lcyA8LSBjKCJBUE9FIiwgIkJDQU4iLCAiQ0VTMSIsICJDSVRFRDEiLAogICAgICAgICAgICAgICAgICAiQ1BNIiwgIkNUU0YiLCAiRENUIiwgIkVETlJCIiwgCiAgICAgICAgICAgICAgICAgICJFR1IxIiwgIkVTUlAxIiwgIkZTVEwxIiwgIk1BTEFUMSIsCiAgICAgICAgICAgICAgICAgICJNQVAySzYiLCAiTUNGMkwiLCAiTUxBTkEiLCAiTVhENCIsCiAgICAgICAgICAgICAgICAgICJPQ0EyIiwgIlBNRUwiLCAiU0VNQTZBIiwgIlNOQUkyIiwKICAgICAgICAgICAgICAgICAgIlNPWDQiLCAiVFNQQU4xMCIpCmVucmljaGVkX3NjIDwtIGVucmljaHIoR2luaV9zY0dlbmVzLCBkYnMpCgpyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSAlaW4lIEdpbmlfc2NHZW5lcwoKYGBgCgoKCiMjIyMgUmVzdCBvZiBKYWNrJ3MgQW5hbHlzaXMgIyMjIwpgYGB7ciBldmFsPUZBTFNFfQojVmlzdWFsbHkgaW5zcGVjdCB0cmVuZGluZyBtZW1iZXJzIGZyb20gaGVhdG1hcHMuCiNQbG90cyBvZiBzcGVjaWZpYyB0cmVuZGluZyBtZW1iZXJzPwpwIDwtIGdncGxvdChkYXRhPWRmMiwgYWVzKHg9ZG9zZSwgeT1sZW4sIGZpbGw9c3VwcCkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yPSJibGFjayIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBOT1RFOiBjb2RlIGJlbG93IHJldXNlcyBvYmplY3QgbmFtZXMuLi4gV0lMTCBPVkVSV1JJVEUhCmBgYHtyIGV2YWw9RkFMU0V9CiNHTE0gQ29lZiBIZWF0bWFwLgpiZXRhcyA8LSBjb2VmKGRkcykKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCm1hdCA8LSBkYXRhLmZyYW1lKGJldGFzW3RvcEdlbmVzLF0pCm1hdCRlbnNlbWJsX2dlbmVfaWQgPSByb3cubmFtZXMobWF0KQptYXQkcGFkaiA9IHJlc1t0b3BHZW5lcywicGFkaiJdCiMgZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIikKIyBtYXJ0IDwtIHVzZURhdGFzZXQoImhzYXBpZW5zX2dlbmVfZW5zZW1ibCIsIG1hcnQgPSBlbnNlbWJsKQojIGdlbmVzID0gcm93Lm5hbWVzKG1hdCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKIyBHRV9kYXRhIDwtIG1lcmdlKG1hdCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQojIHJvd25hbWVzKEdFX2RhdGEpIDwtIG1ha2UubmFtZXMoR0VfZGF0YVssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCiMgR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI1NvcnRpbmcgc2NyaXB0IHRvIHBpY2sgb3V0IGVudHJpZXMgZ3JlYXRlciB0aGFuIG9yIGxlc3MgdGhhbiArLTEKZWcgPSBjKCkKZm9yKGkgaW4gMzoxMCl7CiAgZyA9IHdoaWNoKEdFX2RhdGFbLGldID4gMyB8IEdFX2RhdGFbLGldIDwgLTMpCiAgZWcgPSBjKGVnLGcpCn0KZWcgPSB1bmlxdWUoZWcpCgptYXQgPSBHRV9kYXRhW2VnLC1jKDE6MiwxMSwxMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKIyBsaWJyYXJ5KHBoZWF0bWFwKQpwaGVhdG1hcChtYXQsIGNsdXN0ZXJfY29scyA9IEZBTFNFKQoKIyBzc2RnID0gc2RnWzE6MTAwMCwgXQpkaW0oc2RnKQpoZWFkKHNkZykKCmBgYAoKCg==